Advertisement Create a free account

[Script] Blood And Honor: Automatic blood spatter, pooling and trail effects (lightweight!)

1420449684

Edited 1420769856
Similar to this script , Blood and Honor is an automatic way to add lightweight blood effects to your campaign, without any animations or commands. How it works: Any token in the object layer that has a bar3 (health) value less than or equal to half of its max has a chance to bleed. The chance goes up depending on how severe the damage is. Bleeding occurs in three ways. Spatter Any time a token's health bar is changed so that it's total value is half or less of its maximum, a randomly selected, offset and rotated blood graphic will be placed on the map layer beneath it. The exact formula is as such - the system rolls a dice with as many sides as the max health value. If that roll exceeds the current health value, a blood spatter appears. The larger the amount of damage taken at once, the bigger the spatter will be. Trails If a token moves while at or below half health, a smaller blood graphic will appear where it moves to based on the chances described above. Less health = more blood. Pool If a token's bar3 value falls 0 or below, a large pool of blood graphic appears on the map layer beneath it. ----------------------------------------------------------------- INSTALLATION AND FEATURE DEMONSTRATION <a href="https://www.youtube.com/watch?v=b-BhZRLwOZU&featur" rel="nofollow">https://www.youtube.com/watch?v=b-BhZRLwOZU&featur</a>... ----------------------------------------------------------------- SETTINGS Stop bleeding Putting "noblood" in any token's GM Notes will cause it to never bleed. Clear the map of blood Use the !clearblood command in chat and all blood will move to the top left corner of the map for convenient removal. Change color There are currently three custom colors, but you can easily add your own in the script. Put one of the following into the GM Notes if you want the token to bleed a color other than red: "bloodcolor_orange", "bloodcolor_purple", "bloodcolor_blue" NOTE: YOU MUST SET YOUR OWN IMAGE URLS IN THE SCRIPT! You can put as many of either type (spatters or pools) as you wish, and it will choose from them randomly. See this page for more details. v.0.8 (2015-01-08) Switched order of GM Auth module checks (thanks Brian) [bugfix] ironed out a syntax error v.0.7 (2015-01-08) Size of spatters determined by amount of damage received Addressed stability issue (trails) with cooldown timer v.0.6 (2015-01-07) Added support for TheAaron's GM Auth module v.0.5 (2015-01-06) Added on "ready" event to keep funny things from happening !clearblood command will move all blood to top left corner of map v.0.4 (2015-01-05) [bugfix] "noblood" check now properly passes all bleeding types v.0.3 (2015-01-05) [bugfix] blood pools now appear properly Adding to bar3_value will no longer cause bleeding if the token is still below half health v.0.2 (2015-01-05) Added layer check to make sure nothing bleeds on the non-object layers. Fixed tabs and spacing v.0.1 (2015-01-05) Release ///////////////////////////////////////////////// /***********************************************/ 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/6993500/mAA-8agYIwkhEciVVSCFmg/thumb.png?1420411542" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/6993500/mAA-8agYIwkhEciVVSCFmg/thumb.png?1420411542</a>", ], pools: [ //"<a href="https://s3.amazonaws.com/files.d20.io/images/6993478/77YowTZze57mGAHfSaxwYg/thumb.png?1420411480" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/6993478/77YowTZze57mGAHfSaxwYg/thumb.png?1420411480</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"; 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 && !obj.fbpath) { obj.fbpath = obj.changed._fbpath.replace(/([^\/]*\/){4}/, "/"); } return obj; }; }()); on("ready", function(obj) { setInterval(function(){BloodAndHonor.onTimeout()},1000); on("change:graphic:bar3_value", function(obj, prev) { if (obj.get("bar3_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("bar3_value") &lt;= obj.get("bar3_max") / 2 && prev["bar3_value"] &gt; obj.get("bar3_value") && obj.get("bar3_value") &gt; 0) { if (randomInteger(obj.get("bar3_max")) &gt; obj.get("bar3_value")) { var bloodMult = 1 + ((obj.get("bar3_value") - prev["bar3_value"]) / obj.get("bar3_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("bar3_value") &lt;= 0) { BloodAndHonor.createBlood(obj.get("_pageid"), obj.get("left"), obj.get("top"), Math.floor(BloodAndHonor.tokenSize * 1.5), 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("bar3_value") &lt;= obj.get("bar3_max") / 2 && (obj.get("gmnotes")).indexOf("noblood") == -1) { if (randomInteger(obj.get("bar3_max")) &gt; obj.get("bar3_value")) { BloodAndHonor.createBlood(obj.get("_pageid"), obj.get("left"), obj.get("top"), Math.floor(BloodAndHonor.tokenSize / 2), BloodAndHonor.chooseBlood("spatter"), BloodAndHonor.bloodColor(obj.get("gmnotes"))); BloodAndHonor.timeout += 2; } } } }); on("chat:message", function(msg) { 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); }); } } }); });
1420464424
Ziechael
Forum Champion
Sheet Author
API Scripter
Really interesting script, certainly one i'll be adding and testing, thanks!
I hope you enjoy! I just now updated it with a quick, minor fix that enables the blood pools to work properly. Make sure you grab the latest version, 0.3.
1420468512
Ziechael
Forum Champion
Sheet Author
API Scripter
Got it thanks, is the splatter and pool in your script usable by default or should i overwrite them with my own?
Very nice! I'm tryin to get it working now but it seems that i'm doing something wrong... Really new to the API script tho, so thats problably the reason...This is what i dit: 1) Copied your script 2) uploaded the images in liberary 3) changed the urls 4) testing it. then i get a error...."Sandbox closed due to error.". again great work!
1420478917
Roger A.
Pro
Sheet Author
This sounds awesome!!!!
1420483139

Edited 1420483301
Roger A.
Pro
Sheet Author
I'm curious, would it be possible to have it add blood splatters along the path that the token moved? As far as I can tell this just adds them at the end of their move... am I missing something?
Ziechael said: Got it thanks, is the splatter and pool in your script usable by default or should i overwrite them with my own? No. Thats just an example from my personal library. You must upload your own to your library. I will make an instructional video sometime today. Kars said: Very nice! I'm tryin to get it working now but it seems that i'm doing something wrong... Really new to the API script tho, so thats problably the reason...This is what i dit: 1) Copied your script 2) uploaded the images in liberary 3) changed the urls 4) testing it. then i get a error...."Sandbox closed due to error.". again great work! Can you post or PM me your script? Roger A. said: I'm curious, would it be possible to have it add blood splatters along the path that the token moved? As far as I can tell this just adds them at the end of their move... am I missing something? No you're not missing anything. This is intentional functionality to keep the script light. However, I can easily flag a variable that would do what you ask. I cannot guarantee the script's stability right away if I enable that feature. I'll need some time to hammer it down.
Just posted v.0.4, which is a minor bug fix that shouldn't affect you unless your wanted something to NOT bleed, as the "noblood" check wasn't being passed to trails or pooling.
1420495537
Wes
Pro
Sheet Author
I'm guessing that when you put your URLs in you need to remove the Comment Backslashes? So that this: // Spatters are also used for blood trails. spatters: [ //"<a href="https://s3.amazonaws.com/files.d20.io/images/7006749/f_aAwt5UFhPvB3MdJdLeRg/thumb.png?1420494184" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/7006749/f_aAwt5UFhPvB3MdJdLeRg/thumb.png?1420494184</a>", ], pools: [ //"<a href="https://s3.amazonaws.com/files.d20.io/images/7006747/GLP3JdXOqaFJFPpVXXoUQQ/thumb.png?1420494173" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/7006747/GLP3JdXOqaFJFPpVXXoUQQ/thumb.png?1420494173</a>", ], Becomes this: // Spatters are also used for blood trails. spatters: ["<a href="https://s3.amazonaws.com/files.d20.io/images/7006749/f_aAwt5UFhPvB3MdJdLeRg/thumb.png?1420494184" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/7006749/f_aAwt5UFhPvB3MdJdLeRg/thumb.png?1420494184</a>", ], pools: ["<a href="https://s3.amazonaws.com/files.d20.io/images/7006747/GLP3JdXOqaFJFPpVXXoUQQ/thumb.png?1420494173" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/7006747/GLP3JdXOqaFJFPpVXXoUQQ/thumb.png?1420494173</a>", ], Also the other problem that I noticed is that since I'm using another script to manage my Health Bar: [Script] Alter Bar on Token , that there is a difference in the way the api is handling things, I think a Set Value instead of an On Graphic Change, so that the Blood and Honor Script doesn't notice the change of the health bar. Thanks for this this script, it's really cool!
1420496438

Edited 1420496455
Wes said: I'm guessing that when you put your URLs in you need to remove the Comment Backslashes? So that this: // Spatters are also used for blood trails. spatters: [ //"<a href="https://s3.amazonaws.com/files.d20.io/images/7006749/f_aAwt5UFhPvB3MdJdLeRg/thumb.png?1420494184" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/7006749/f_aAwt5UFhPvB3MdJdLeRg/thumb.png?1420494184</a>", ], pools: [ //"<a href="https://s3.amazonaws.com/files.d20.io/images/7006747/GLP3JdXOqaFJFPpVXXoUQQ/thumb.png?1420494173" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/7006747/GLP3JdXOqaFJFPpVXXoUQQ/thumb.png?1420494173</a>", ], Becomes this: // Spatters are also used for blood trails. spatters: ["<a href="https://s3.amazonaws.com/files.d20.io/images/7006749/f_aAwt5UFhPvB3MdJdLeRg/thumb.png?1420494184" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/7006749/f_aAwt5UFhPvB3MdJdLeRg/thumb.png?1420494184</a>", ], pools: ["<a href="https://s3.amazonaws.com/files.d20.io/images/7006747/GLP3JdXOqaFJFPpVXXoUQQ/thumb.png?1420494173" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/7006747/GLP3JdXOqaFJFPpVXXoUQQ/thumb.png?1420494173</a>", ], Also the other problem that I noticed is that since I'm using another script to manage my Health Bar: [Script] Alter Bar on Token , that there is a difference in the way the api is handling things, I think a Set Value instead of an On Graphic Change, so that the Blood and Honor Script doesn't notice the change of the health bar. Thanks for this this script, it's really cool! You're correct - take out the comment forwardslashes. I really need to get that video up, but I need to get some caffeine first or it's going to be terrible! And yes, my script listens specifically for change:graphic:bar3_value and change:graphic:lastmove. I'll see if I can hook into the Target.set functionality that he uses to set the bars in that script. Shouldn't be that hard. :)
omg THIS script is awesome! I love these kind of addon effects you can do :3
esp with the flicker lights script
1420499269

Edited 1420501242
Okay Wes! I have a fix for you, and anyone else using Honeybader's [Script] Alter Bar on Token . With the two scripts properly installed, you'll need to inject a bit of code into the Alter Bar script: // 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 && 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 This will go between lines 90 and 91 of his script, so the finished product will look like this: // VERSION INFO var AlterBars_Version = 1.1; // FUNCTION DECLARATION var AlterScript = AlterScript || {}; on("chat:message", function (msg) { // Exit if not an api command... if (msg.type != "api") return; // Get the api command... var command = msg.content.split(" ", 1)[0]; if (command === "!alter") AlterScript.Process(msg); }); AlterScript.Process = function(msg, who) { // USER CONFIG // BAR CONFIGURATION - These are used to identify which bar to adjust. You can // also use any lowercase letters as well such as 'h' for hit points/health. var Bar1Key = "1"; var Bar2Key = "2"; var Bar3Key = "3"; // BAR NAMES - These names are used if ANNOUNCE_CHANGE is set to true. The // format of the annoucement is: Name gained/lost # BarName. var BarNames = ["hit points", "hit points", "hit points"]; // PREVENT OVERMAX - Set this variable to true to prevent the current value of // the bar from being greater than its max value. If there is no max value set, // it will not stop the current bar value from increasing. var PREVENT_OVERMAX = true; // ANNOUNCE CHANGE IN CHAT - Set to true to send a message to the chat window // showing which token gained or lost points and how much. var ANNOUNCE_CHANGE = true; // SEND TO GM - Set to true to send the results to the GM. This will also trigger // if a hidden change is sent. var ALERT_GM = false; // STOP AT ZERO - Prevents the current value of the bar from dropping below zero. var STOP_AT_ZERO = true; // END USER CONFIG // Get and/or create variables... var n = msg.content.split(" "); var who = msg.who; var Target = getObj("graphic", n[1]); var Bar = 0; Bar = (n[2].toLowerCase().toString() == Bar1Key) ? 1 : 0; Bar = (n[2].toLowerCase().toString() == Bar2Key) ? 2 : Bar; Bar = (n[2].toLowerCase().toString() == Bar3Key) ? 3 : Bar; if (Bar === 0) { sendChat("ERROR", "/w " + who.replace(" (GM)", "") + " That is not a valid bar."); return; } var AlterValue = n[3]; var CurrentValue = parseInt(Target.get("bar" + Bar + "_value")); var MaxValue = parseInt(Target.get("bar" + Bar + "_max")); var NoAnnounce = n[4]; // Check for a + or - sign... var Operand1 = AlterValue.charAt(0); var Operand2 = AlterValue.charAt(1); if (Operand2 === "+" || Operand2 === "-") AlterValue = AlterValue.substring(2); else if (Operand1 === "+" || Operand1 === "-") AlterValue = AlterValue.substring(1); // Save the value for the tooltip... var Expression = AlterValue; // Define CSS... var AddStyle = "display: inline-block; text-align: center; min-width: 1.75em; font-size: 1em; font-weight: bold; color:#040; background-color: #8C8; border: 1px solid #040; padding: -1px 2px; border-radius: 3px;"; var MinusStyle = "display: inline-block; text-align: center; min-width: 1.75em; font-size: 1em; font-weight: bold; color:#600; background-color: #FAA; border: 1px solid #600; padding: -1px 2px; border-radius: 3px;"; // Main process... sendChat("", "/r " + AlterValue, function(outs) { AlterValue = parseInt(JSON.parse(outs[0].content).total); var Tooltip = "Rolling " + Expression + " = " + AlterValue + "' class='a inlinerollresult showtip tipsy-n'"; if (Operand1 != "-") { // Add to bar... if (PREVENT_OVERMAX) AlterValue = (AlterValue + CurrentValue &gt; MaxValue) ? MaxValue - CurrentValue : AlterValue; if (ANNOUNCE_CHANGE && NoAnnounce == undefined) sendChat("DiceBot", Target.get("name") + " gained &lt;span title='" + Tooltip + "' style='" + AddStyle + "'&gt;" + AlterValue + "&lt;/span&gt; " + BarNames[Bar-1] + "."); if (ALERT_GM || NoAnnounce != undefined) sendChat(who, "/w GM " + Target.get("name") + " gained &lt;span title='" + Tooltip + "' style='" + AddStyle + "'&gt;" + AlterValue + "&lt;/span&gt; " + BarNames[Bar-1] + "."); Target.set("bar" + Bar + "_value", CurrentValue += AlterValue); } else { // Subtract from bar... if (STOP_AT_ZERO && (CurrentValue - AlterValue &lt; 0)) AlterValue = CurrentValue; if (ANNOUNCE_CHANGE && NoAnnounce == undefined) sendChat("DiceBot", Target.get("name") + " lost &lt;span title='" + Tooltip + "' style='" + MinusStyle + "'&gt;" + AlterValue + "&lt;/span&gt; " + BarNames[Bar-1] + "."); if (ALERT_GM || NoAnnounce != undefined) sendChat(who, "/w GM " + Target.get("name") + " lost &lt;span title='" + Tooltip + "' style='" + MinusStyle + "'&gt;" + AlterValue + "&lt;/span&gt; " + BarNames[Bar-1] + "."); Target.set("bar" + Bar + "_value", CurrentValue -= AlterValue); // 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 && 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 } }); }; And that's it! I've tested it and it runs great on my campaign. I might even keep his script because of the nice CSS box :p.
1420500439
Wes
Pro
Sheet Author
Awesome! Thanks for sorting that out so quickly! Yeah that's one of the reasons I use it, the other being that its so easy to click a token and a macro and not have to fiddle with the silly little circle!
Fix it and it is working perfectly now. Great job and thanks John!!! The instruction video was a great help,you got a like from me, and I hope that more scriptcreators will do this now(helps out newbies, like myself, allot!).
I've been thinking of making something similiar. You've done it better than I could! Good job!
Kars said: Fix it and it is working perfectly now. Great job and thanks John!!! The instruction video was a great help,you got a like from me, and I hope that more scriptcreators will do this now(helps out newbies, like myself, allot!). Glad to hear it! Tristan B. said: I've been thinking of making something similiar. You've done it better than I could! Good job! There's always room for expansion and optimization. I've learned a lot about coding from just staying involved, and there is tons I still don't know about Javascript that can probably make parts of my scripts run faster. Feel free to fool around and try things!
Maybe its a idea to add the option for the gm to clear all blood with one command, It can be a messy playground after a battle.
Nice script ! This works perfect for what i needed it for was making a wounded enemy the party was tracking so was just randomly throwing bloodstains where needed, this made everything alot easier and now I am thinking about just keeping it in the campaign for all fights or some other interesting ways to use it. I agree with Kars that would be a cool feature and I actually have an idea about that Ill test it out and let you know if it pans out. Anyway nice work and thanks :D
Kars said: Maybe its a idea to add the option for the gm to clear all blood with one command, It can be a messy playground after a battle. The best I can do is move all the bloodstains to one place or hide them on the GM layer, or both. There's no API to destroy an object at the moment, which is problematic for a lot of things. Kevin K.(Desu) said: Nice script ! This works perfect for what i needed it for was making a wounded enemy the party was tracking so was just randomly throwing bloodstains where needed, this made everything alot easier and now I am thinking about just keeping it in the campaign for all fights or some other interesting ways to use it. I agree with Kars that would be a cool feature and I actually have an idea about that Ill test it out and let you know if it pans out. Anyway nice work and thanks :D Glad you're getting some use out of it!
Kars said: Maybe its a idea to add the option for the gm to clear all blood with one command, It can be a messy playground after a battle. Update v.0.5 added! Use the !clearblood command to move all the blood to the top left corner of the map for easy deletion. I had planned on this functionality in one form or the other, so it's retroactive. Any existing blood will be moved as well.
Awesome was just playing with doing just that moving it all to one corner and dumping it in the GM layer to delete easy. Thnaks for update
John C. said: Kars said: Maybe its a idea to add the option for the gm to clear all blood with one command, It can be a messy playground after a battle. Update v.0.5 added! Use the !clearblood command to move all the blood to the top left corner of the map for easy deletion. I had planned on this functionality in one form or the other, so it's retroactive. Any existing blood will be moved as well. Great work!
1420634884

Edited 1439540721
Ziechael
Forum Champion
Sheet Author
API Scripter
Awesome video tutorial, say hi to your player for us lol! Really helped me understand how to tweak this to suit my campaign, i'm seeing lots of green and black blood for those nasty fiends ahead :) Great work. OH-oh, i broke it... i've added 2 colours (black and green) and all my own spatters/pools (10/2 ratio) and am getting this error: undefined:1303. throw "Error: toFront() must be given an object either from an event or ge. ^.Error: toFront() must be given an object either from an event or getObj() or similar. Here is my full code: ///////////////////////////////////////////////// /***********************************************/ 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.5", 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>", /***********************************************/ ///////////////////////////////////////////////// // 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, // 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/med.png?1420637338" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/7029416/E1Ltkjvnl2vRpTy8tEzizw/med.png?1420637338</a>", "<a href="https://s3.amazonaws.com/files.d20.io/images/7029415/MLsoOwvMDa1JVYgUvW6B9w/med.png?1420637338" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/7029415/MLsoOwvMDa1JVYgUvW6B9w/med.png?1420637338</a>", "<a href="https://s3.amazonaws.com/files.d20.io/images/7029414/3MoaSCA5oJn-KFQOrJlb_g/med.png?1420637338" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/7029414/3MoaSCA5oJn-KFQOrJlb_g/med.png?1420637338</a>", "<a href="https://s3.amazonaws.com/files.d20.io/images/7029466/_Dj9KCZTwQYK08viNGVZ0g/med.png?1420637808" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/7029466/_Dj9KCZTwQYK08viNGVZ0g/med.png?1420637808</a>", "<a href="https://s3.amazonaws.com/files.d20.io/images/7029417/M6y2P0exUcLHrQPOlEd71g/med.png?1420637339" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/7029417/M6y2P0exUcLHrQPOlEd71g/med.png?1420637339</a>", "<a href="https://s3.amazonaws.com/files.d20.io/images/7029418/1O_RWFB-xkPaElbaM6JitA/med.png?1420637339" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/7029418/1O_RWFB-xkPaElbaM6JitA/med.png?1420637339</a>", "<a href="https://s3.amazonaws.com/files.d20.io/images/7029421/x8RnxTkCbU-eqbo4MqV0mw/med.png?1420637342" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/7029421/x8RnxTkCbU-eqbo4MqV0mw/med.png?1420637342</a>", "<a href="https://s3.amazonaws.com/files.d20.io/images/7029420/NgEX0Bg3WQ9y7XgKTEpZdQ/med.png?1420637342" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/7029420/NgEX0Bg3WQ9y7XgKTEpZdQ/med.png?1420637342</a>", "<a href="https://s3.amazonaws.com/files.d20.io/images/7029422/AGMNRwd_BvGFLjgjBGhaFA/med.png?1420637343" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/7029422/AGMNRwd_BvGFLjgjBGhaFA/med.png?1420637343</a>", "<a href="https://s3.amazonaws.com/files.d20.io/images/7029423/qTAdlint-YlNDfqU9eVUeQ/med.png?1420637343" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/7029423/qTAdlint-YlNDfqU9eVUeQ/med.png?1420637343</a>", ], pools: [ "<a href="https://s3.amazonaws.com/files.d20.io/images/7029424/icwaJtDz4RlBArCKZ436Kg/med.png?1420637356" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/7029424/icwaJtDz4RlBArCKZ436Kg/med.png?1420637356</a>", "<a href="https://s3.amazonaws.com/files.d20.io/images/7029425/WA8d9mQEUCv6bzSdqKcydA/med.png?1420637356" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/7029425/WA8d9mQEUCv6bzSdqKcydA/med.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 "#008000"; if (gmnotes.indexOf("bloodcolor_black") !== -1) return "#000000"; 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); }, }; 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) { on("change:graphic:bar3_value", function(obj, prev) { if (obj.get("bar3_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("bar3_value") &lt;= obj.get("bar3_max") / 2 && prev["bar3_value"] &gt; obj.get("bar3_value") && obj.get("bar3_value") &gt; 0) { if (randomInteger(obj.get("bar3_max")) &gt; obj.get("bar3_value")) { BloodAndHonor.createBlood(obj.get("_pageid"), obj.get("left"), obj.get("top"), BloodAndHonor.tokenSize, BloodAndHonor.chooseBlood("spatter"), BloodAndHonor.bloodColor(obj.get("gmnotes"))); } } // Create pool near token if health drops below 1. else if (obj.get("bar3_value") &lt;= 0) { BloodAndHonor.createBlood(obj.get("_pageid"), obj.get("left"), obj.get("top"), Math.floor(BloodAndHonor.tokenSize * 1.5), 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 (obj.get("bar3_value") &lt;= obj.get("bar3_max") / 2 && (obj.get("gmnotes")).indexOf("noblood") == -1) { if (randomInteger(obj.get("bar3_max")) &gt; obj.get("bar3_value")) { BloodAndHonor.createBlood(obj.get("_pageid"), obj.get("left"), obj.get("top"), Math.floor(BloodAndHonor.tokenSize / 2), BloodAndHonor.chooseBlood("spatter"), BloodAndHonor.bloodColor(obj.get("gmnotes"))); } } }); on("chat:message", function(msg) { if(msg.type == "api" && msg.content.indexOf("!clearblood") !== -1) { objects = filterObjs(function(obj) {&nbsp; 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); }); } }); });
1420649662
Roger A.
Pro
Sheet Author
you have to change the image names from med.png to thumb.png or it wont create the images. Then when it tries to do the next step it doesn't have anything to work with so it fails.
1420649857
The Aaron
Forum Champion
API Scripter
Man, I can't count the number of times that has bit me in the ass...
v.0.6 released, added support for The Aaron's GM Auth module. Enable by setting useIsGm to true.
1420694515
The Aaron
Forum Champion
API Scripter
Cool! =D
Made the official move to the wiki in light of the official API repository announcement . Version v.0.7 released, spatters will now be larger depending on the percentage of health lost in that single hit. Stability concerns addressed by adding a cooldown to the "trails" effect (stops flooding the server when multiple players are moving at once, still needs as much testing as possible).
1420758025
Brian
Sheet Author
API Scripter
John C. said: if (isGM(msg.playerid) == false && BloodAndHonor.useIsGM == true) These two conditions need to be flipped. You don't want the system to try and use the nonexistent isGM function if the script isn't installed, so you want to short-circuit the evaluation by moving the useIsGM check first. if (BloodAndHonor.useIsGM && !isGM(msg.playerid))
I've copied the script in and I'm getting this error: Unexpected token } I still get the error even when I've got all of my other scripts disabled (I am using IsGM).
Brian said: John C. said: if (isGM(msg.playerid) == false && BloodAndHonor.useIsGM == true) These two conditions need to be flipped. You don't want the system to try and use the nonexistent isGM function if the script isn't installed, so you want to short-circuit the evaluation by moving the useIsGM check first. if (BloodAndHonor.useIsGM && !isGM(msg.playerid)) Thank you, added the fix. Andrew (Kruvek) said: I've copied the script in and I'm getting this error: Unexpected token } I still get the error even when I've got all of my other scripts disabled (I am using IsGM). New updated v.0.8 fixes this issue as well. Should have parsed it. Was tired.
1420800981

Edited 1420820135
Ziechael
Forum Champion
Sheet Author
API Scripter
Roger A. said: you have to change the image names from med.png to thumb.png or it wont create the images. Then when it tries to do the next step it doesn't have anything to work with so it fails. I knew it would be something i've done... even looked at that bit and thought to myself, it seems right, i don't want to mess with it and break anything ;) Will update to 0.8, change my links and cross my fingers! edit: For reference, works like a treat now, you = star :)
1421043983
Man, this is a cool script, and after playing around with it a bit, I'm totally going to implement it in my game to make things more interesting. A little bit of a request, though; Is it possible to add a chunk that sets a specific image for when a token is reduced to 0 health? And/or if the "X" icon is applied? I generally use a specific blood image to denote where enemies died so I can remove the token to keep the board from being cluttered, and having a script do this automatically would be pretty sweet.
1421050788

Edited 1421050843
Roger A.
Pro
Sheet Author
The pool category is what gets used for tokens at or below 0hp, and is only used for that if I'm not mistaken.... only put the url for the pool(or other image) you want to use in the pools url section and you should be set. Unless I missed something.