Skip to content

Instantly share code, notes, and snippets.

@dsibilly
Created January 27, 2021 00:39
Show Gist options
  • Save dsibilly/9842464eea8f69fcea5597e084144ed2 to your computer and use it in GitHub Desktop.
Save dsibilly/9842464eea8f69fcea5597e084144ed2 to your computer and use it in GitHub Desktop.
Roll20 API script for automatically marking tokens as bloodied or dead
/**
* dead_and_bloodied.js v1.0
* Duane Sibilly <[email protected]>
*
* Description: Listens for token bar changes to identify when tokens are
* bloodied (reduced to half HP or less) and dead (reduced to 0 HP or less.)
*/
on('ready', () => {
const barNumber = 1, // Set this to the bar you're using for HP: 1, 2, or 3
barObject = { // This represents the bar as an abstract object
value: 0.0,
max: 0.0,
// Some convenience methods to perform bloodied & death calculations
isBloodied: function () {
return this.value <= this.max / 2 && this.value > 0;
},
isDead: function () {
return this.value <= 0;
},
},
bloodied = 'red', // Set this to the id of the token marker representing bloodied
dead = 'dead', // Set this to the id of the token marker representing dead
scriptName = 'dead_and_bloodied.js',
// unpacks the status marker string into an object
unpackStatusMarkers = stats => stats.split(/,/).reduce((statusObject, value) => {
let statusName = value.split(/@/),
statusNumber;
statusName[0] = statusName[0].toLowerCase();
statusNumber = parseInt(statusName[1] || '0', 10);
if (statusName[0].length) {
statusObject[statusName[0]] = Math.max(statusNumber, statusObject[statusName[0]] || 0);
}
return statusObject;
}, {}),
// serializes a status marker object into a comma-delimited string
packStatusMarkers = statusObject => Object.keys(statusObject).map(status => {
if (status === 'dead' || statusObject[status] === true || statusObject[status] < 1 || statusObject[status] > 9) {
// If the status is a name, a Boolean, or outside the bounds [1, 9], do nothing
return status;
}
// If the status is a Number, turn it into name@number
return `${status}@${parseInt(statusObject[status], 10)}`;
}).join(','),
// handler callback for bar changes
barChangeHandler = tokenObject => {
const statusMarkers = unpackStatusMarkers(tokenObject.get('statusmarkers'));
barObject.value = parseFloat(tokenObject.get(`bar${barNumber}_value`));
barObject.max = parseFloat(tokenObject.get(`bar${barNumber}_max`));
if (Number.isNaN(barObject.max)) {
// If the bar has no max set, do nothing
return;
}
// Update bloodied status
if (barObject.isBloodied()) {
statusMarkers[bloodied] = true;
} else {
delete statusMarkers[bloodied];
}
// Update dead status
if (barObject.isDead()) {
statusMarkers[dead] = true;
} else {
delete statusMarkers[dead];
}
// Serialize and save status changes to the token
tokenObject.set({
statusmarkers: packStatusMarkers(statusMarkers)
});
};
// When the selected bar changes, fire off the handler callback!
on(`change:graphic:bar${barNumber}_value`, barChangeHandler);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment