I actually have a version that works well with Its-a-Trap. Try this one out after putting in some of your own graphics. (I'm not the author. I just picked this up somewhere when last this discussion was up. :D ) var BloodSpatter = {
version: '0.1.0',
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>',
hpBar: 3, //1, 2, or 3
hpCountUp: false,
baseSize: 0.75,
// This will make it so only the GM can use the !clearblood command. Change to "true" if you want to check for authorization.
onlyAllowGMtoRunCommands: true,
// 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_avatar_property_restrictions" rel="nofollow">https://wiki.roll20.net/API:Objects#imgsrc_and_avatar_property_restrictions</a>
// You can add as many as you'd like to either category. Spatters are also used for blood trails.
spatters: [
{'Your-own-graphic-here'},
],
pools: [
{
'src': 'Your-bloodpool-graphic-here',
'width': 100,
'height': 100
},
],
chooseBlood: function (type) {
if (type === 'spatter') {
return BloodSpatter.spatters[randomInteger(BloodSpatter.spatters.length) - 1];
}
if (type === 'pool') {
return BloodSpatter.pools[randomInteger(BloodSpatter.pools.length) - 1];
}
},
getOffset: function () {
return Math.random() < 0.5 ? 1 : -1;
},
bloodColor: function (gmnotes) {
if (gmnotes.indexOf('bloodcolor_purple') !== -1) {
return '#20124d';
} else if (gmnotes.indexOf('bloodcolor_blue') !== -1) {
return '#00ffff';
} else if (gmnotes.indexOf('bloodcolor_orange') !== -1) {
return '#ffff00';
} else if (gmnotes.indexOf('bloodcolor_green') !== -1) {
return '#274e13';
} else if (gmnotes.indexOf('bloodcolor_black') !== -1) {
return '000000';
} else {
return '#a61c00';
}
},
createBlood: function (token, multiplier, bloodType) {
var pages = _.union([Campaign().get('playerpageid')], _.values(Campaign().get('playerspecificpages')));
var dropBlood = _.contains(pages, token.get('pageid'));
var gmNotes = token.get('gmnotes');
if (!dropBlood || token.get('layer') !== 'objects' || gmNotes.indexOf('noblood') !== -1) {
return;
}
var size = Math.floor(BloodSpatter.getTokenSize(token) * multiplier);
var bloodImage = BloodSpatter.chooseBlood(bloodType);
var bloodTokenSource = bloodImage;
var bloodTokenWidth = size * multiplier;
var bloodTokenHeight = size * multiplier;
if (bloodImage !== null && typeof(bloodImage) === 'object') {
bloodTokenSource = bloodImage.src;
var bloodImageAspectRatio = bloodImage.width / bloodImage.height;
var widthRatioMultiplier = 1;
var heightRatioMultiplier = 1;
if (bloodImageAspectRatio < 1) {
widthRatioMultiplier = bloodImageAspectRatio;
} else if (bloodImageAspectRatio > 1) {
heightRatioMultiplier = bloodImage.height / bloodImage.width;
}
bloodTokenWidth = bloodTokenWidth * widthRatioMultiplier;
bloodTokenHeight = bloodTokenHeight * heightRatioMultiplier;
}
var widthIncrement = bloodTokenWidth * 0.1;
var widthIncrementTotal = widthIncrement;
var heightIncrement = bloodTokenHeight * 0.1;
var heightIncrementTotal = heightIncrement;
var spatterToken = BloodSpatter.fixedCreateObj('graphic', {
pageid: token.get('_pageid'),
imgsrc: bloodTokenSource,
tint_color: BloodSpatter.bloodColor(gmNotes),
gmnotes: 'blood',
top: token.get('top') + (randomInteger(Math.floor(size / 2)) * BloodSpatter.getOffset()),
left: token.get('left') + (randomInteger(Math.floor(size / 2)) * BloodSpatter.getOffset()),
width: widthIncrement,
height: heightIncrement,
rotation: randomInteger(360) - 1,
layer: 'map'
});
toFront(spatterToken);
(function splatterEnlargeFunction(count) {
if (count < 10) {
setTimeout(function () {
widthIncrementTotal += widthIncrement;
heightIncrementTotal += heightIncrement;
spatterToken.set({
width: widthIncrementTotal,
height: heightIncrementTotal
});
splatterEnlargeFunction(count + 1);
}, 30);
}
})(0);
},
getTokenSize: function (token) {
return (token.get('height') + token.get('width')) / 2; //average the height and the width
},
timeout: 0,
increaseTimeout: function () {
BloodSpatter.timeout += 4;
BloodSpatter.watchTimeout();
},
interval: null,
watchTimeout: function () {
if (BloodSpatter.interval === null) {
BloodSpatter.interval = setInterval(function () {
if (BloodSpatter.timeout > 0) {
BloodSpatter.timeout--;
} else {
clearInterval(BloodSpatter.interval);
BloodSpatter.interval = null;
}
}, 2000);
}
},
fixedCreateObj: function () {
return function () {
var obj = createObj.apply(this, arguments);
if (obj && !obj.fbpath) {
obj.fbpath = obj.changed._fbpath.replace(/([^\/]*\/){4}/, '/');
}
return obj;
};
}(),
tokenHPChanged: function (token, maxHealth, currentHealth, damageTaken) {
if (maxHealth === '') {
return;
}
var percentOfHpLost = damageTaken / maxHealth;
var damageMultiplier = BloodSpatter.baseSize + percentOfHpLost;
if ((!BloodSpatter.hpCountUp && currentHealth <= maxHealth / 2) || (BloodSpatter.hpCountUp && currentHealth >= maxHealth / 2)) {
token.set({
status_red: true
});
// Create spatter near token if "bloodied". Chance of spatter depends on severity of damage
if (damageTaken > 0 && currentHealth > 0 && ( (!BloodSpatter.hpCountUp && currentHealth < randomInteger(maxHealth)) || (BloodSpatter.hpCountUp && currentHealth > randomInteger(maxHealth)) )) {
BloodSpatter.createBlood(token, damageMultiplier, 'spatter');
}
} else {
token.set({
status_red: false
});
}
if ((!BloodSpatter.hpCountUp && currentHealth <= 0) || (BloodSpatter.hpCountUp && currentHealth >= maxHealth)) {
token.set({
status_skull: true
});
// Create pool near token if health drops below 1.
if (damageTaken > 0) {
BloodSpatter.createBlood(token, damageMultiplier, 'pool');
}
} else {
token.set({
status_skull: false
});
}
}
};
on('ready', function () {
on('change:graphic:bar' + BloodSpatter.hpBar + '_value', function (token, prev) {
var maxHealth = token.get('bar' + BloodSpatter.hpBar + '_max');
var currentHealth = token.get('bar' + BloodSpatter.hpBar + '_value');
var previousHealth = prev['bar' + BloodSpatter.hpBar + '_value'];
var damageTaken;
if (!BloodSpatter.hpCountUp) {
damageTaken = previousHealth - currentHealth;
} else {
damageTaken = currentHealth - previousHealth;
}
BloodSpatter.tokenHPChanged(token, maxHealth, currentHealth, damageTaken);
});
//Make blood trails, chance goes up depending on how injured a token is
on('change:graphic:lastmove', function (token) {
var maxHealth = token.get('bar' + BloodSpatter.hpBar + '_max');
if (maxHealth === '' || BloodSpatter.timeout !== 0) {
return;
}
var currentHealth = token.get('bar' + BloodSpatter.hpBar + '_value');
var healthLost = maxHealth - currentHealth;
var percentOfHpLost = healthLost / maxHealth;
var damageMultiplier = (BloodSpatter.baseSize / 2) + percentOfHpLost;
if ((!BloodSpatter.hpCountUp && currentHealth <= maxHealth / 2 && currentHealth < randomInteger(maxHealth)) || (BloodSpatter.hpCountUp && currentHealth >= maxHealth / 2 && currentHealth > randomInteger(maxHealth))) {
BloodSpatter.createBlood(token, damageMultiplier, 'spatter');
BloodSpatter.increaseTimeout();
}
});
on('chat:message', function (msg) {
if (msg.type === 'api' && (msg.content.indexOf('!clearblood') !== -1 || msg.content.indexOf('!clear blood') !== -1)) {
if (BloodSpatter.onlyAllowGMtoRunCommands && !playerIsGM(msg.playerid)) {
sendChat(msg.who, '/w ' + msg.who + ' You are not authorized to use that command!');
return;
} else {
var objects = filterObjs(function (obj) {
if (obj.get('type') === 'graphic' && obj.get('gmnotes') === 'blood') {
return true;
} else {
return false;
}
});
_.each(objects, function (obj) {
obj.remove();
});
}
}
});
});