Skip to content

Instantly share code, notes, and snippets.

@joesinghaus joesinghaus/ApplyDamage.js Secret
Last active Jun 30, 2020

Embed
What would you like to do?
ApplyDamage
/* global log, _, getObj, HealthColors, playerIsGM, sendChat, on */
const ApplyDamage = (() => {
"use strict";
const version = "1.1",
observers = {
"change": [],
},
boundedBar = false,
checkInstall = () => {
log(`-=> ApplyDamage v${version} <=-`);
},
defaultOpts = {
type: "half",
ids: "",
saves: "",
DC: "-1",
dmg: "0",
bar: "1"
},
statusMarkers = [
"red", "blue", "green", "brown", "purple", "pink", "yellow", "dead", "skull", "sleepy", "half-heart",
"half-haze", "interdiction", "snail", "lightning-helix", "spanner", "chained-heart", "chemical-bolt",
"death-zone", "drink-me", "edge-crack", "ninja-mask", "stopwatch", "fishing-net", "overdrive", "strong",
"fist", "padlock", "three-leaves", "fluffy-wing", "pummeled", "tread", "arrowed", "aura", "back-pain",
"black-flag", "bleeding-eye", "bolt-shield", "broken-heart", "cobweb", "broken-shield", "flying-flag",
"radioactive", "trophy", "broken-skull", "frozen-orb", "rolling-bomb", "white-tower", "grab", "screaming",
"grenade", "sentry-gun", "all-for-one", "angel-outfit", "archery-target"
],
getWhisperPrefix = (playerid) => {
const player = getObj("player", playerid);
if (player && player.get("_displayname")) {
return `/w "${player.get("_displayname")}" `;
}
else {
return "/w GM ";
}
},
parseOpts = (content, hasValue) => {
return content
.replace(/<br\/>\n/g, " ")
.replace(/({{(.*?)\s*}}\s*$)/g, "$2")
.split(/\s+--/)
.slice(1)
.reduce((opts, arg) => {
const kv = arg.split(/\s(.+)/);
if (hasValue.includes(kv[0])) {
opts[kv[0]] = (kv[1] || "");
} else {
opts[arg] = true;
}
return opts;
}, {});
},
processInlinerolls = function (msg) {
if (msg.inlinerolls && msg.inlinerolls.length) {
return msg.inlinerolls.map(v => {
const ti = v.results.rolls.filter(v2 => v2.table)
.map(v2 => v2.results.map(v3 => v3.tableItem.name).join(", "))
.join(", ");
return (ti.length && ti) || v.results.total || 0;
}).reduce((m, v, k) => m.replace(`$[[${k}]]`, v), msg.content);
} else {
return msg.content;
}
},
handleError = (whisper, errorMsg) => {
const output = `${whisper}<div style="border:1px solid black;background:#FFBABA;padding:3px">` +
`<h4>Error</h4><p>${errorMsg}</p></div>`;
sendChat("ApplyDamage", output);
},
finalApply = (results, dmg, type, bar, status) => {
const barCur = `bar${bar}_value`,
barMax = `bar${bar}_max`;
Object.entries(results).forEach(([id, saved]) => {
const token = getObj("graphic", id),
prev = JSON.parse(JSON.stringify(token || {}));
let newValue;
if (token && !saved) {
if (boundedBar) {
newValue = Math.min(Math.max(parseInt(token.get(barCur)) - dmg, 0), parseInt(token.get(barMax)));
} else {
newValue = parseInt(token.get(barCur)) - dmg;
}
if (status) token.set(`status_${status}`, true);
}
else if (token && type === "half") {
if (boundedBar) {
newValue = Math.min(Math.max(parseInt(token.get(barCur)) - Math.floor(dmg / 2), 0), parseInt(token.get(barMax)));
} else {
newValue = parseInt(token.get(barCur)) - Math.floor(dmg / 2);
}
}
if (!_.isUndefined(newValue)) {
if (Number.isNaN(newValue)) newValue = token.get(barCur);
token.set(barCur, newValue);
notifyObservers("change", token, prev);
}
});
},
handleInput = (msg) => {
if (msg.type === "api" && msg.content.search(/^!apply-damage\b/) !== -1) {
const hasValue = ["ids", "saves", "DC", "type", "dmg", "bar", "status"],
opts = Object.assign({}, defaultOpts, parseOpts(processInlinerolls(msg), hasValue));
opts.ids = opts.ids.split(/,\s*/g);
opts.saves = opts.saves.split(/,\s*/g);
opts.DC = parseInt(opts.DC);
opts.dmg = parseInt(opts.dmg);
if (!playerIsGM(msg.playerid) && getObj("player", msg.playerid)) {
handleError(getWhisperPrefix(msg.playerid), "Permission denied.");
return;
}
if (!["1", "2", "3"].includes(opts.bar)) {
handleError(getWhisperPrefix(msg.playerid), "Invalid bar.");
return;
}
if (opts.status === "none") {
delete opts.status;
}
if (opts.status && !statusMarkers.includes(opts.status)) {
handleError(getWhisperPrefix(msg.playerid), "Invalid status.");
return;
}
const results = _.reduce(opts.ids, function (m, id, k) {
m[id] = parseInt(opts.saves[k] || "0") >= opts.DC;
return m;
}, {});
finalApply(results, opts.dmg, opts.type, opts.bar, opts.status);
const output = `${
getWhisperPrefix(msg.playerid)
}<div style="border:1px solid black;background:#FFF;padding:3px"><p>${
(opts.dmg ? `${opts.dmg} damage applied to tokens, with ${
(opts.type === "half" ? "half" : "no")
} damage on a successful saving throw.` : "")}${
(opts.status ? ` ${opts.status} status marker applied to tokens that failed the save.` : "")
}</p></div>`;
sendChat("ApplyDamage", output, null, { noarchive: true });
}
return;
},
notifyObservers = (event, obj, prev) => {
observers[event].forEach(observer => observer(obj, prev));
},
registerObserver = (event, observer) => {
if (observer && _.isFunction(observer) && observers.hasOwnProperty(event)) {
observers[event].push(observer);
} else {
log("ApplyDamage event registration unsuccessful.");
}
},
registerEventHandlers = () => {
on("chat:message", handleInput);
};
return {
checkInstall,
registerEventHandlers,
registerObserver
};
})();
on("ready", () => {
"use strict";
ApplyDamage.checkInstall();
ApplyDamage.registerEventHandlers();
if ("undefined" !== typeof HealthColors) {
ApplyDamage.registerObserver("change", HealthColors.Update);
}
});
@Bragunetzki

This comment has been minimized.

Copy link

Bragunetzki commented May 11, 2020

Is there any way I can make this apply damage to a specific bar?

@BronxBob

This comment has been minimized.

Copy link

BronxBob commented May 25, 2020

I was having trouble getting the "-- bar" to work in macros so I just changed line 18 to default 3. Would the proper syntax be "!apply-damage --bar3"? hmm also it is being put in a button... so brackets of somekind are needed?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.