Roll20 uses cookies to improve your experience on our site. Cookies enable you to enjoy certain features, social sharing functionality, and tailor message and display ads to your interests on our site and others. They also help us understand how our site is being used. By continuing to use our site, you consent to our use of cookies. Update your cookie preferences .
×
Create a free account

GroupCheck and ApplyDamage with Statusmarkers

I stumbled across this older thread on the topic when trying to figure this out today, but has anyone found a way to use custom status markers when using the GroupCheck and ApplyDamage scripts?  The default statusmarkers are OK, but we don't use those and are already used to the two custom sets that we have in the game.  I can use Tokenmod to apply the custom statusmarkers, but I was really hoping to find a way to just integrate this into the ApplyDamage based on the saving throws from GroupCheck.  
1625515142
The Aaron
Roll20 Production Team
API Scripter
I have no idea if this works, but I think I modified ApplyDamage to support libTokenMarkers (in the 1-click), which would let you use Custom Token Markers.  Here's the code I have, let me know if it works for you: /* global log, _, getObj, HealthColors, playerIsGM, sendChat, on, libTokenMarkers */ const ApplyDamage = (() => { const version = "1.2"; const observers = { "change": [] }; let boundedBar = false; const defaultOpts = { type: "half", ids: "", saves: "", DC: "-1", dmg: "0", bar: "1" }; const checkInstall = () => { log(`-=> ApplyDamage v${version} <=-`); if('undefined' === typeof libTokenMarkers || (['getStatus','getStatuses','getOrderedList'].find(k=> !libTokenMarkers.hasOwnProperty(k) || 'function' !== typeof libTokenMarkers[k] )) ) { // notify of the missing library sendChat('',`/w gm <div style="color:red;font-weight:bold;border:2px solid red;background-color:black;border-radius:1em;padding:1em;">Missing dependency: libTokenMarkers</div>`); } }; const getWhisperPrefix = (playerid) => { const player = getObj("player", playerid); if (player && player.get("_displayname")) { return `/w "${player.get("_displayname")}" `; } else { return "/w GM "; } }; const 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; }, {}); }; const 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; } }; const 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); }; const 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) { let tm = libTokenMarkers.getStatus(status); token.set(`status_${tm.getTag()}`, 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); } }); }; const 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 && 0 === libTokenMarkers.getStatuses(opts.status).length) { 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; }; const notifyObservers = (event, obj, prev) => { observers[event].forEach(observer => observer(obj, prev)); }; const registerObserver = (event, observer) => { if (observer && _.isFunction(observer) && observers.hasOwnProperty(event)) { observers[event].push(observer); } else { log("ApplyDamage event registration unsuccessful."); } }; const registerEventHandlers = () => { on("chat:message", handleInput); }; on("ready", () => { checkInstall(); registerEventHandlers(); if ("undefined" !== typeof HealthColors) { ApplyDamage.registerObserver("change", HealthColors.Update); } }); return { registerObserver }; })();
1625516830

Edited 1625517048
Didn't seem to change anything for me.
Wow.  I am just in awe of your skills.  Amazing.  Yes, this worked great!  Thanks so much! The Aaron said: I have no idea if this works, but I think I modified ApplyDamage to support libTokenMarkers (in the 1-click), which would let you use Custom Token Markers.  Here's the code I have, let me know if it works for you:
1625524272
The Aaron
Roll20 Production Team
API Scripter
Ah, fantastic.  I actually have no idea how to run this to test it, so I'm glad it worked out. =D
What macro are you using to get this to work. As it doesn't want to work for me?
Kilter said: What macro are you using to get this to work. As it doesn't want to work for me? I Disabled the old ApplyDamage API and used this updated one instead.  You also need the libTokenMarkers which I already had installed to support TokenNumberName.  And then I use this macro to run the GroupCheck/ApplyDamage, although you may need to change the statusmarker names to match whatever custom set you have: !group-check --whisper {{ --?{Ability Save|Strength,Strength Save|Dexterity,Dexterity Save|Constitution,Constitution Save|Intelligence,Intelligence Save|Wisdom,Wisdom Save|Charisma,Charisma Save} --ro ?{Roll Type|Normal,roll1|Advantage,adv|Disadvantage,dis} --process --subheader vs DC ?{DC} --button ApplyDamage !apply-damage ~dmg [[?{Damage|0}]] ~type ?{Damage on Save|Half,half|None,none} ~DC ?{DC} ~saves RESULTS(,) ~ids IDS(,) ?{Status| None,~| Prone,~status prone| Restrained,~status restrained| Grappled,~status grappled| Incapacitated,~status incapacitated| Stunned,~status stunned| Unconscious,~status unconcious| ―, | Faerie Fire,~status aid-2| Charmed,~status charmed| Frightened,~status fear| ―, | Poisoned,~status poisoned| Blinded,~status blind| Deafened,~status deaf| Paralyzed,~status incapacitated| Petrified,~status petrified| Slowed,~status slowed| ―, } }}
Thank you, I was still using the full name for the marker.