diff --git a/Plugger/1.0.9/Plugger.js b/Plugger/1.0.9/Plugger.js
new file mode 100644
index 0000000000..daa7307d60
--- /dev/null
+++ b/Plugger/1.0.9/Plugger.js
@@ -0,0 +1,674 @@
+/*
+=========================================================
+Name : Plugger
+GitHub : https://github.com/TimRohr22/Cauldron/tree/master/Plugger
+Roll20 Contact : timmaugh
+Version : 1.0.9
+Last Update : 17 MAY 2024
+=========================================================
+*/
+var API_Meta = API_Meta || {};
+API_Meta.Plugger = { offset: Number.MAX_SAFE_INTEGER, lineCount: -1 };
+{
+ try { throw new Error(''); } catch (e) { API_Meta.Plugger.offset = (parseInt(e.stack.split(/\n/)[1].replace(/^.*:(\d+):.*$/, '$1'), 10) - (13)); }
+}
+
+const Plugger = (() => {
+ const apiproject = 'Plugger';
+ const version = '1.0.9';
+ const schemaVersion = 0.1;
+ API_Meta[apiproject].version = version;
+ const vd = new Date(1715952845199);
+ const versionInfo = () => {
+ log(`\u0166\u0166 ${apiproject} v${API_Meta[apiproject].version}, ${vd.getFullYear()}/${vd.getMonth() + 1}/${vd.getDate()} \u0166\u0166 -- offset ${API_Meta[apiproject].offset}`);
+ if (!state.hasOwnProperty(apiproject) || state[apiproject].version !== schemaVersion) {
+ log(` > Updating ${apiproject} Schema to v${schemaVersion} <`);
+ switch (state[apiproject] && state[apiproject].version) {
+
+ case 0.1:
+ /* break; // intentional dropthrough */ /* falls through */
+
+ case 'UpdateSchemaVersion':
+ state[apiproject].version = schemaVersion;
+ break;
+
+ default:
+ state[apiproject] = {
+ version: schemaVersion,
+ };
+ break;
+ }
+ }
+ };
+ const logsig = () => {
+ // initialize shared namespace for all signed projects, if needed
+ state.torii = state.torii || {};
+ // initialize siglogged check, if needed
+ state.torii.siglogged = state.torii.siglogged || false;
+ state.torii.sigtime = state.torii.sigtime || Date.now() - 3001;
+ if (!state.torii.siglogged || Date.now() - state.torii.sigtime > 3000) {
+ const logsig = '\n' +
+ ' _____________________________________________ ' + '\n' +
+ ' )_________________________________________( ' + '\n' +
+ ' )_____________________________________( ' + '\n' +
+ ' ___| |_______________| |___ ' + '\n' +
+ ' |___ _______________ ___| ' + '\n' +
+ ' | | | | ' + '\n' +
+ ' | | | | ' + '\n' +
+ ' | | | | ' + '\n' +
+ ' | | | | ' + '\n' +
+ ' | | | | ' + '\n' +
+ '______________|_|_______________|_|_______________' + '\n' +
+ ' ' + '\n';
+ log(`${logsig}`);
+ state.torii.siglogged = true;
+ state.torii.sigtime = Date.now();
+ }
+ return;
+ };
+
+ const nestlog = (stmt, ilvl = 0, logcolor = '', boolog = false) => {
+ if (isNaN(ilvl)) {
+ ilvl = 0;
+ log(`Next statement fed a NaN value for the indentation.`);
+ }
+ if ((state[apiproject] && state[apiproject].logging === true) || boolog) {
+ let l = `${Array(ilvl + 1).join("==")}${stmt}`;
+ if (logcolor) {
+ // l = /:/.test(l) ? `${l.replace(/:/, ':')}` : `${l}`;
+ }
+ log(l);
+ }
+ };
+
+ const escapeRegExp = (string) => { return string.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&'); };
+ const assertstart = rx => new RegExp(`^${rx.source}`, rx.flags);
+ const getfirst = (cmd, ...args) => {
+ // pass in objects of form: {type: 'text', rx: /regex/}
+ // return object of form : {regex exec object with property 'type': 'text'}
+
+ let ret = {};
+ let r;
+ args.find(a => {
+ r = a.rx.exec(cmd);
+ if (r && (!ret.length || r.index < ret.index)) {
+ ret = Object.assign(r, { type: a.type });
+ }
+ a.lastIndex = 0;
+ }, ret);
+ return ret;
+ };
+
+ // REGEX STATEMENTS =====================================
+ const evalrx = /(\()?{&\s*eval(?:\((?[^)]+)\)){0,1}\s*}((?<=\({&\s*eval(?:\(([^)]+)\)){0,1}\s*})\)|\1)\s*/i,
+ evalendrx = /(\()?{&\s*\/\s*eval\s*}((?<=\({&\s*\/\s*eval\s*})\)|\1)/i;
+
+ // TAG RX SETS REGISTRY =================================
+ const tagrxset = {
+ 'eval': { opentag: evalrx, endtag: evalendrx }
+ };
+
+ // TOKEN MARKERS ========================================
+ const eostm = { rx: /$/, type: 'eos' },
+ evaltm = { rx: evalrx, type: 'eval' },
+ evalendtm = { rx: evalendrx, type: 'evalend' };
+
+ // END TOKEN REGISTRY ===================================
+ const endtokenregistry = {
+ main: [eostm],
+ eval: [evalendtm],
+ };
+
+ const tokenizeOps = (msg, msgstate, status, notes) => {
+ class TextToken {
+ constructor() {
+ this.type = 'text';
+ this.escape = '';
+ this.value = '';
+ }
+ }
+ class PlugEvalToken {
+ constructor() {
+ this.type = 'eval';
+ this.contents = [];
+ }
+ }
+
+ const getTextToken = (c) => {
+ let logcolor = 'lawngreen';
+ nestlog(`TEXT INPUT: ${c.cmd}`, c.indent, logcolor, msgstate.logging);
+ let markers = [];
+ c.looptype = c.looptype || '';
+ switch (c.looptype) {
+ case 'eval':
+ default:
+ markers = [evaltm, evalendtm, eostm];
+ break;
+ }
+ let res = getfirst(c.cmd, ...markers);
+ let index = res.index;
+ let token = new TextToken();
+ token.value = c.cmd.slice(0, index);
+ nestlog(`TEXT KEEPS: ${token.value}`, c.indent, logcolor, msgstate.logging);
+ return { token: token, index: index };
+ };
+ const getPlugEvalToken = (c) => {
+ // receives object in the form of:
+ // {cmd: command line slice, indent: #, overallindex: #, looptype: text}
+ let logcolor = 'yellow';
+ let index = 0;
+ let evalopenres = tagrxset[c.looptype].opentag.exec(c.cmd);
+ if (evalopenres) {
+ nestlog(`${c.looptype.toUpperCase()} TOKEN INPUT: ${c.cmd}`, c.indent, logcolor, msgstate.logging);
+ let token = new PlugEvalToken();
+ token.escape = evalopenres.groups && evalopenres.groups.escape && evalopenres.groups.escape.length ? evalopenres.groups.escape : '';
+ let index = evalopenres[0].length;
+
+ // content and nested evals
+ nestlog(`BUILDING CONTENT: ${c.cmd.slice(index)}`, c.indent + 1, 'lightseagreen', msgstate.logging);
+ let contentres = evalval({ cmd: c.cmd.slice(index), indent: c.indent + 1, type: c.looptype, overallindex: c.overallindex + index, looptype: c.looptype });
+ if (contentres.error) return contentres;
+ token.contents = contentres.tokens;
+ index += contentres.index;
+ nestlog(`ENDING CONTENT: ${c.cmd.slice(index)}`, c.indent + 1, 'lightseagreen', msgstate.logging);
+
+ // closing bracket of eval tag
+ let evalendres = tagrxset[c.looptype].endtag.exec(c.cmd.slice(index));
+ if (!evalendres) {
+ status.push('unresolved');
+ notes.push(`Unexpected token at ${c.overallindex + index}. Expected end of ${c.looptype.toUpperCase()} structure ('{& eval}'), but saw: ${c.cmd.slice(index, index + 10)}`);
+ return { error: `Unexpected token at ${c.overallindex + index}. Expected end of ${c.looptype.toUpperCase()} structure ('{& eval}'), but saw: ${c.cmd.slice(index, index + 10)}` };
+ }
+ index += evalendres[0].length;
+ nestlog(`${c.looptype.toUpperCase()} TOKEN OUTPUT: ${JSON.stringify(token)}`, c.indent, logcolor, msgstate.logging);
+ return { token: token, index: index };
+ } else {
+ status.push('unresolved');
+ notes.push(`Unexpected token at ${c.overallindex + index}. Expected an ${c.looptype.toUpperCase()} structure, but saw: ${c.cmd.slice(index, index + 10)}`);
+ return { error: `Unexpected token at ${c.overallindex + index}. Expected an ${c.looptype.toUpperCase()} structure, but saw: ${c.cmd.slice(index, index + 10)}` };
+ }
+ };
+ const evalval = c => {
+ // expects an object in the form of:
+ // { cmd: text, indent: #, overallindex: #, type: text, overallindex: #, looptype: text }
+ let tokens = []; // main output array
+ let logcolor = 'aqua';
+ let loopstop = false;
+ let tokenres = {};
+ let index = 0;
+ let loopindex = 0;
+ nestlog(`${c.looptype.toUpperCase()} BEGINS`, c.indent, logcolor, msgstate.logging);
+ while (!loopstop) {
+ loopindex = index;
+ if (assertstart(tagrxset[c.looptype].opentag).test(c.cmd.slice(index))) {
+ status.push('changed');
+ tokenres = getPlugEvalToken({ cmd: c.cmd.slice(index), indent: c.indent + 1, overallindex: c.overallindex + index, looptype: c.looptype });
+ } else {
+ tokenres = getTextToken({ cmd: c.cmd.slice(index), indent: c.indent + 1, overallindex: c.overallindex + index, looptype: c.looptype });
+ }
+ if (tokenres) {
+ if (tokenres.error) { return tokenres; }
+ tokens.push(tokenres.token);
+ index += tokenres.index;
+ }
+ if (loopindex === index) { // nothing detected, loop never ends
+ return { error: `Unexpected token at ${c.overallindex + index}.` };
+ }
+ loopstop = (getfirst(c.cmd.slice(index), ...endtokenregistry[c.type]).index === 0);
+ }
+ nestlog(`${c.looptype.toUpperCase()} ENDS`, c.indent, logcolor, msgstate.logging);
+ return { tokens: tokens, index: index };
+ };
+
+ return evalval({ cmd: msg.content, indent: 0, type: 'main', overallindex: 0, looptype: 'eval' });
+ };
+
+ const reconstructOps = (o, msg, msgstate, status, notes) => {
+ const runPlugin = c => {
+ const evalstmtrx = /^\s*(?