From 31232b2bd866948dbe84a424dac72e2ff469631f Mon Sep 17 00:00:00 2001 From: timmaugh Date: Tue, 20 Jul 2021 16:03:59 -0400 Subject: [PATCH 1/2] Ordering bug fix --- Fetch/1.0.8/Fetch.js | 718 +++++++++++++++++++++++++++ Fetch/Fetch.js | 10 +- Fetch/script.json | 5 +- MathOps/1.0.4/MathOps.js | 316 ++++++++++++ MathOps/MathOps.js | 10 +- MathOps/script.json | 7 +- Muler/1.0.7/Muler.js | 346 +++++++++++++ Muler/Muler.js | 12 +- Muler/script.json | 5 +- Plugger/1.0.5/Plugger.js | 483 ++++++++++++++++++ Plugger/Plugger.js | 12 +- Plugger/script.json | 7 +- SelectManager/1.0.8/SelectManager.js | 559 +++++++++++++++++++++ SelectManager/SelectManager.js | 10 +- SelectManager/script.json | 12 +- 15 files changed, 2475 insertions(+), 37 deletions(-) create mode 100644 Fetch/1.0.8/Fetch.js create mode 100644 MathOps/1.0.4/MathOps.js create mode 100644 Muler/1.0.7/Muler.js create mode 100644 Plugger/1.0.5/Plugger.js create mode 100644 SelectManager/1.0.8/SelectManager.js diff --git a/Fetch/1.0.8/Fetch.js b/Fetch/1.0.8/Fetch.js new file mode 100644 index 0000000000..559bc01cb7 --- /dev/null +++ b/Fetch/1.0.8/Fetch.js @@ -0,0 +1,718 @@ +/* +========================================================= +Name : Fetch +GitHub : https://github.com/TimRohr22/Cauldron/tree/master/Fetch +Roll20 Contact : timmaugh +Version : 1.0.8 +Last Update : 7/20/2021 +========================================================= +*/ +var API_Meta = API_Meta || {}; +API_Meta.Fetch = { offset: Number.MAX_SAFE_INTEGER, lineCount: -1 }; +{ + try { throw new Error(''); } catch (e) { API_Meta.Fetch.offset = (parseInt(e.stack.split(/\n/)[1].replace(/^.*:(\d+):.*$/, '$1'), 10) - (13)); } +} + +const Fetch = (() => { + const apiproject = 'Fetch'; + const version = '1.0.8'; + const schemaVersion = 0.1; + API_Meta[apiproject].version = version; + const vd = new Date(1626810300264); + 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 escapeRegExp = (string) => { return string.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&'); }; + 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; + }; + const getEditDistance = (a, b) => { + if (a.length === 0) return b.length; + if (b.length === 0) return a.length; + + var matrix = []; + + // increment along the first column of each row + var i; + for (i = 0; i <= b.length; i++) { + matrix[i] = [i]; + } + + // increment each column in the first row + var j; + for (j = 0; j <= a.length; j++) { + matrix[0][j] = j; + } + + // Fill in the rest of the matrix + for (i = 1; i <= b.length; i++) { + for (j = 1; j <= a.length; j++) { + if (b.charAt(i - 1) === a.charAt(j - 1)) { + matrix[i][j] = matrix[i - 1][j - 1]; + } else { + matrix[i][j] = Math.min(matrix[i - 1][j - 1] + 1, // substitution + Math.min(matrix[i][j - 1] + 1, // insertion + matrix[i - 1][j] + 1)); // deletion + } + } + } + + return matrix[b.length][a.length]; + }; + const repeatingOrdinal = (character_id, section = '', attr_name = '') => { + if (!section && !attr_name) return; + let ordrx, match; + if (attr_name) { + ordrx = /^repeating_([^_]+)_([^_]+)_.*$/; + if (!ordrx.test(attr_name)) return; // the supplied attribute name isn't a repeating attribute at all + match = ordrx.exec(attr_name); + section = match[1]; + } + let sectionrx = new RegExp(`repeating_${section}_([^_]+)_.*$`); + let createOrderKeys = [...new Set(findObjs({ type: 'attribute', characterid: character_id }) + .filter(a => sectionrx.test(a.get('name'))) + .map(a => sectionrx.exec(a.get('name'))[1]))]; + let sortOrderKeys = (findObjs({ type: 'attribute', characterid: character_id, name: `_reporder_repeating_${section}` })[0] || { get: () => { return ''; } }) + .get('current') + .split(/\s*,\s*/) + .filter(a => createOrderKeys.includes(a)); + sortOrderKeys.push(...createOrderKeys.filter(a => !sortOrderKeys.includes(a))); + return attr_name ? sortOrderKeys.indexOf(match[2]) : sortOrderKeys; + }; + const parsePattern = (cmd) => { + const fieldcomprx = /^((?m)\s*\|)?\s*^(?[^\s]+?)\s*(?>=|<=|~|!~|=|!=|<|>)\s*((`|'|")(?.*?)\6|(?.*?)(?=\s|$))\s*/i; + const fieldrx = /^((?m)\s*\|)?\s*(?[^\s]+)\s*/i; + const fieldcomptm = { rx: fieldcomprx, type: 'fieldcomp' }, + fieldtm = { rx: fieldrx, type: 'field' }; + let index = 0; + let p = {}; + let tokens = []; + while (!/^$/.test(cmd.slice(index))) { + p = getfirst(cmd.slice(index), fieldcomptm, fieldtm); + if (p) { + if (p.type === 'field') tokens.push({ type: '=', contents: [p.groups.field, true], retrieve: p.groups.retrieve ? 'max' : 'current' }); + else tokens.push({ type: p.groups.operator, contents: [p.groups.field, p.groups.value || p.groups.altvalue], retrieve: p.groups.retrieve ? 'max' : 'current' }); + index += p[0].length; + } else { + return { tokens: [], error: `Unexpected token encountered in repeating pattern: ${cmd}` }; + } + } + return { tokens: tokens }; + }; + + const getSheetItem = (res, pid, char) => { + // expects result of the getFirst() function, a rx result with a type property + // r.type === 'sheetitem' + const itemTypeLib = { + '@': 'attribute', + '*': 'attribute', + '%': 'ability' + } + let c = char || getChar(res.groups.character, pid); + if (!c) return; + // standard sheet items + if (['@', '%'].includes(res.groups.type)) { + let sheetobj = findObjs({ type: itemTypeLib[res.groups.type], characterid: c.id }) + .filter(a => a.get('name') === res.groups.item)[0]; + return sheetobj; + } + // if we're still here, we're looking for a repeating item + let p = parsePattern(res.groups.pattern); + if (!p.tokens.length) { + log(p.error || 'No pattern detected for repeating sheet item.'); + return; + } + + let filterLib = { + '=': (a) => a.contents[0] == a.contents[1], + '!=': (a) => a.contents[0] != a.contents[1], + '~': (a) => a.contents[0].includes(a.contents[1]), + '!~': (a) => !a.contents[0].includes(a.contents[1]), + '>': (a) => (internalTestLib.num(a.contents[0]) ? Number(a.contents[0]) : a.contents[0]) > (internalTestLib.num(a.contents[1]) ? Number(a.contents[1]) : a.contents[1]), + '>=': (a) => (internalTestLib.num(a.contents[0]) ? Number(a.contents[0]) : a.contents[0]) >= (internalTestLib.num(a.contents[1]) ? Number(a.contents[1]) : a.contents[1]), + '<': (a) => (internalTestLib.num(a.contents[0]) ? Number(a.contents[0]) : a.contents[0]) < (internalTestLib.num(a.contents[1]) ? Number(a.contents[1]) : a.contents[1]), + '<=': (a) => (internalTestLib.num(a.contents[0]) ? Number(a.contents[0]) : a.contents[0]) <= (internalTestLib.num(a.contents[1]) ? Number(a.contents[1]) : a.contents[1]) + } + + p.tests = []; + let reprx = new RegExp(`^repeating_${res.groups.section}_(?[^_]*?)_(?.+)$`); + let repres; + let o = findObjs({ type: itemTypeLib[res.groups.type], characterid: c.id }) + .filter(a => reprx.test(a.get('name'))); + o.forEach(a => { + reprx.lastIndex = 0; + repres = reprx.exec(a.get('name')); + a.name = a.get('name'); + a.repID = repres.groups.repID; + a.suffix = repres.groups.suffix; + }); + + let viable = []; + p.tokens.forEach(s => { + viable = []; + o.forEach(a => { + if (a.suffix.toLowerCase() === s.contents[0].toLowerCase()) { + if (filterLib[s.type]({ contents: [a.get(s.retrieve), s.contents[1]] })) viable.push(a.repID); + } + }); + p.tests.push(viable); + }); + // we should have the same number of tests as we do testable conditions + if (p.tests.length !== p.tokens.length) { + log(`EXITING: TEST COUNTS DON'T MATCH`); + return; + } + viable = p.tests.reduce((m, v) => m.filter(repID => v.includes(repID))); + if (viable.length) { + let retObj = findObjs({ type: itemTypeLib[res.groups.type], characterid: c.id }) + .filter(a => a.get('name') === `repeating_${res.groups.section}_${viable[0]}_${res.groups.valuesuffix}`)[0]; + return retObj; + } + }; + const getSheetItemVal = (res, pid, char) => { + // expects the result of a rx with groups + let val = '', + retrieve = '', + o = {}; + // determine what to test; also what to retrieve if another value isn't specified + if (['@', '*'].includes(res.groups.type) && res.groups.valtype !== 'max') { + retrieve = 'current'; + } else if (['@', '*'].includes(res.groups.type)) { + retrieve = 'max'; + } else { + retrieve = 'action'; + } + // determine if a different retrievable info is requested + if (res.groups.type === '*' && res.groups.valtype === 'name$') { + retrieve = 'name$'; + } else if (res.groups.type === '*' && res.groups.valtype === 'row$') { + retrieve = 'row$'; + } else if (res.groups.valtype === 'rowid') { + retrieve = 'rowid'; + } else if (res.groups.valtype === 'name') { + retrieve = 'name'; + } else if (res.groups.valtype === 'id') { + retrieve = 'id'; + } + // go get the item + o = getSheetItem(res, pid, char); + if (!o) { + return; + } else { + if (['name', 'action', 'current', 'max', 'id'].includes(retrieve)) { + val = o.get(retrieve); + } else { + val = o.get('name'); + let row; + let rptrx = /^repeating_([^_]+)_([^_]+)_(.*)$/i; + let rptres = rptrx.exec(val) || [undefined, undefined, '', '']; + switch (retrieve) { + case 'row$': + val = `$${repeatingOrdinal(o.get('characterid'), undefined, val)}`; + break; + case 'name$': + row = `$${repeatingOrdinal(o.get('characterid'), undefined, val)}`; + val = `repeating_${rptres[1]}_${row}_${rptres[3]}`; + break; + case 'rowid': + val = rptres[2]; + break; + default: + } + } + } + return val; + }; + + const getChar = (query, pid) => { // find a character where info is an identifying piece of information (id, name, or token id) + let character; + let qrx = new RegExp(escapeRegExp(query), 'i'); + let charsIControl = findObjs({ type: 'character' }); + charsIControl = playerIsGM(pid) ? charsIControl : charsIControl.filter(c => c.get('controlledby').split(',').includes(pid)); + character = charsIControl.filter(c => c.id === query)[0] || + charsIControl.filter(c => c.id === (getObj('graphic', query) || { get: () => { return '' } }).get('represents'))[0] || + charsIControl.filter(c => c.get('name') === query)[0] || + charsIControl.filter(c => qrx.test(c)).reduce((m, v) => { + let d = getEditDistance(query, v); + return !m.length || d < m[1] ? [v, d] : m; + }, [])[0]; + return character; + }; + const getPageID = (pid) => { + return playerIsGM(pid) ? getObj('player', pid).get('_lastpage') : Campaign().get('playerpageid'); + }; + const getToken = (info, pgid = '') => { + return findObjs({ type: 'graphic', subtype: 'token', id: info })[0] || + findObjs({ type: 'graphic', subtype: 'token', name: info, pageid: pgid })[0]; + }; + const getObjName = (key, type) => { + let o; + switch (type) { + case 'playerlist': + o = key.split(/\s*,\s*/) + .map(k => k === 'all' ? k : getObj('player', k)) + .filter(c => c) + .map(c => c === 'all' ? c : c.get('displayname')) + .join(', '); + return o.length ? o : undefined; + case 'page': + case 'attribute': + case 'character': + default: + o = getObj(type, key); + return o ? o.get('name') : undefined; + } + }; + const tokenProps = { + cardid: { refersto: '_cardid', permissionreq: 'any', dataval: (d) => d }, + cid: { refersto: '_cardid', permissionreq: 'any', dataval: (d) => d }, + tid: { refersto: '_id', permissionreq: 'any', dataval: (d) => d }, + token_id: { refersto: '_id', permissionreq: 'any', dataval: (d) => d }, + pageid: { refersto: '_pageid', permissionreq: 'any', dataval: (d) => d }, + pid: { refersto: '_pageid', permissionreq: 'any', dataval: (d) => d }, + page: { refersto: '_pageid', permissionreq: 'any', dataval: d => getObjName(d, 'page') }, + page_name: { refersto: '_pageid', permissionreq: 'any', dataval: d => getObjName(d, 'page') }, + sub: { refersto: '_subtype', permissionreq: 'any', dataval: (d) => d }, + subtype: { refersto: '_subtype', permissionreq: 'any', dataval: (d) => d }, + token_type: { refersto: '_type', permissionreq: 'any', dataval: (d) => d }, + adv_fow_view_distance: { refersto: 'adv_fow_view_distance', permissionreq: 'any', dataval: (d) => d }, + aura1: { refersto: 'aura1_color', permissionreq: 'any', dataval: (d) => d }, + aura1_color: { refersto: 'aura1_color', permissionreq: 'any', dataval: (d) => d }, + aura1_radius: { refersto: 'aura1_radius', permissionreq: 'any', dataval: (d) => d }, + radius1: { refersto: 'aura1_radius', permissionreq: 'any', dataval: (d) => d }, + aura1_square: { refersto: 'aura1_square', permissionreq: 'any', dataval: (d) => d }, + square1: { refersto: 'aura1_square', permissionreq: 'any', dataval: (d) => d }, + aura2: { refersto: 'aura2_color', permissionreq: 'any', dataval: (d) => d }, + aura2_color: { refersto: 'aura2_color', permissionreq: 'any', dataval: (d) => d }, + aura2_radius: { refersto: 'aura2_radius', permissionreq: 'any', dataval: (d) => d }, + radius2: { refersto: 'aura2_radius', permissionreq: 'any', dataval: (d) => d }, + aura2_square: { refersto: 'aura2_square', permissionreq: 'any', dataval: (d) => d }, + square2: { refersto: 'aura2_square', permissionreq: 'any', dataval: (d) => d }, + bar_location: { refersto: 'bar_location', permissionreq: 'any', dataval: (d) => d }, + bar_loc: { refersto: 'bar_location', permissionreq: 'any', dataval: (d) => d }, + bar1_link: { refersto: 'bar1_link', permissionreq: 'any', dataval: (d) => d }, + link1: { refersto: 'bar1_link', permissionreq: 'any', dataval: (d) => d }, + bar1_name: { refersto: 'bar1_link', permissionreq: 'any', dataval: d => /^sheetattr_/.test(d) ? d.replace(/^sheetattr_/, '') : getObjName(d, 'attribute') }, + name1: { refersto: 'bar1_link', permissionreq: 'any', dataval: d => /^sheetattr_/.test(d) ? d.replace(/^sheetattr_/, '') : getObjName(d, 'attribute') }, + bar1_max: { refersto: 'bar1_max', permissionreq: 'any', dataval: (d) => d }, + max1: { refersto: 'bar1_max', permissionreq: 'any', dataval: (d) => d }, + bar1: { refersto: 'bar1_value', permissionreq: 'any', dataval: (d) => d }, + bar1_current: { refersto: 'bar1_value', permissionreq: 'any', dataval: (d) => d }, + bar1_value: { refersto: 'bar1_value', permissionreq: 'any', dataval: (d) => d }, + bar2_link: { refersto: 'bar2_link', permissionreq: 'any', dataval: (d) => d }, + link2: { refersto: 'bar2_link', permissionreq: 'any', dataval: (d) => d }, + bar2_name: { refersto: 'bar2_link', permissionreq: 'any', dataval: d => /^sheetattr_/.test(d) ? d.replace(/^sheetattr_/, '') : getObjName(d, 'attribute') }, + name2: { refersto: 'bar2_link', permissionreq: 'any', dataval: d => /^sheetattr_/.test(d) ? d.replace(/^sheetattr_/, '') : getObjName(d, 'attribute') }, + bar2_max: { refersto: 'bar2_max', permissionreq: 'any', dataval: (d) => d }, + max2: { refersto: 'bar2_max', permissionreq: 'any', dataval: (d) => d }, + bar2: { refersto: 'bar2_value', permissionreq: 'any', dataval: (d) => d }, + bar2_current: { refersto: 'bar2_value', permissionreq: 'any', dataval: (d) => d }, + bar2_value: { refersto: 'bar2_value', permissionreq: 'any', dataval: (d) => d }, + bar3_link: { refersto: 'bar3_link', permissionreq: 'any', dataval: (d) => d }, + link3: { refersto: 'bar3_link', permissionreq: 'any', dataval: (d) => d }, + bar3_name: { refersto: 'bar3_link', permissionreq: 'any', dataval: d => /^sheetattr_/.test(d) ? d.replace(/^sheetattr_/, '') : getObjName(d, 'attribute') }, + name3: { refersto: 'bar3_link', permissionreq: 'any', dataval: d => /^sheetattr_/.test(d) ? d.replace(/^sheetattr_/, '') : getObjName(d, 'attribute') }, + bar3_max: { refersto: 'bar3_max', permissionreq: 'any', dataval: (d) => d }, + max3: { refersto: 'bar3_max', permissionreq: 'any', dataval: (d) => d }, + bar3: { refersto: 'bar3_value', permissionreq: 'any', dataval: (d) => d }, + bar3_current: { refersto: 'bar3_value', permissionreq: 'any', dataval: (d) => d }, + bar3_value: { refersto: 'bar3_value', permissionreq: 'any', dataval: (d) => d }, + bright_light_distance: { refersto: 'bright_light_distance', permissionreq: 'any', dataval: (d) => d }, + compact_bar: { refersto: 'compact_bar', permissionreq: 'any', dataval: (d) => d }, + token_cby: { refersto: 'controlledby', permissionreq: 'any', dataval: (d) => d }, + token_controlledby: { refersto: 'controlledby', permissionreq: 'any', dataval: (d) => d }, + token_cby_names: { refersto: 'controlledby', permissionreq: 'any', dataval: d => getObjName(d, 'playerlist') }, + token_controlledby_names: { refersto: 'controlledby', permissionreq: 'any', dataval: d => getObjName(d, 'playerlist') }, + token_cby_name: { refersto: 'controlledby', permissionreq: 'any', dataval: d => getObjName(d, 'playerlist') }, + token_controlledby_name: { refersto: 'controlledby', permissionreq: 'any', dataval: d => getObjName(d, 'playerlist') }, + currentside: { refersto: 'currentSide', permissionreq: 'any', dataval: (d) => d }, + curside: { refersto: 'currentSide', permissionreq: 'any', dataval: (d) => d }, + side: { refersto: 'currentSide', permissionreq: 'any', dataval: (d) => d }, + dim_light_opacity: { refersto: 'dim_light_opacity', permissionreq: 'any', dataval: (d) => d }, + directional_bright_light_center: { refersto: 'directional_bright_light_center', permissionreq: 'any', dataval: (d) => d }, + directional_bright_light_total: { refersto: 'directional_bright_light_total', permissionreq: 'any', dataval: (d) => d }, + directional_low_light_center: { refersto: 'directional_low_light_center', permissionreq: 'any', dataval: (d) => d }, + directional_low_light_total: { refersto: 'directional_low_light_total', permissionreq: 'any', dataval: (d) => d }, + emits_bright: { refersto: 'emits_bright_light', permissionreq: 'any', dataval: (d) => d }, + emits_bright_light: { refersto: 'emits_bright_light', permissionreq: 'any', dataval: (d) => d }, + emits_low: { refersto: 'emits_low_light', permissionreq: 'any', dataval: (d) => d }, + emits_low_light: { refersto: 'emits_low_light', permissionreq: 'any', dataval: (d) => d }, + fliph: { refersto: 'fliph', permissionreq: 'any', dataval: (d) => d }, + flipv: { refersto: 'flipv', permissionreq: 'any', dataval: (d) => d }, + gmnotes: { refersto: 'gmnotes', permissionreq: 'gm', dataval: (d) => unescape(d) }, + has_bright_light_vision: { refersto: 'has_bright_light_vision', permissionreq: 'any', dataval: (d) => d }, + has_directional_bright_light: { refersto: 'has_directional_bright_light', permissionreq: 'any', dataval: (d) => d }, + has_directional_low_light: { refersto: 'has_directional_low_light', permissionreq: 'any', dataval: (d) => d }, + has_limit_field_of_night_vision: { refersto: 'has_limit_field_of_night_vision', permissionreq: 'any', dataval: (d) => d }, + has_limit_field_of_vision: { refersto: 'has_limit_field_of_vision', permissionreq: 'any', dataval: (d) => d }, + has_night_vision: { refersto: 'has_night_vision', permissionreq: 'any', dataval: (d) => d }, + has_nv: { refersto: 'has_night_vision', permissionreq: 'any', dataval: (d) => d }, + nv_has: { refersto: 'has_night_vision', permissionreq: 'any', dataval: (d) => d }, + height: { refersto: 'height', permissionreq: 'any', dataval: (d) => d }, + imgsrc: { refersto: 'imgsrc', permissionreq: 'any', dataval: (d) => d }, + drawing: { refersto: 'isdrawing', permissionreq: 'any', dataval: (d) => d }, + isdrawing: { refersto: 'isdrawing', permissionreq: 'any', dataval: (d) => d }, + lastmove: { refersto: 'lastmove', permissionreq: 'any', dataval: (d) => d }, + lastx: { refersto: 'lastmove', permissionreq: 'any', dataval: d => d.split(/\s*,\s*/)[0] || '' }, + lasty: { refersto: 'lastmove', permissionreq: 'any', dataval: d => d.split(/\s*,\s*/)[1] || '' }, + layer: { refersto: 'layer', permissionreq: 'gm', dataval: (d) => d }, + left: { refersto: 'left', permissionreq: 'any', dataval: (d) => d }, + light_angle: { refersto: 'light_angle', permissionreq: 'any', dataval: (d) => d }, + light_dimradius: { refersto: 'light_dimradius', permissionreq: 'any', dataval: (d) => d }, + light_hassight: { refersto: 'light_hassight', permissionreq: 'any', dataval: (d) => d }, + light_losangle: { refersto: 'light_losangle', permissionreq: 'any', dataval: (d) => d }, + light_multiplier: { refersto: 'light_multiplier', permissionreq: 'any', dataval: (d) => d }, + light_otherplayers: { refersto: 'light_otherplayers', permissionreq: 'any', dataval: (d) => d }, + light_radius: { refersto: 'light_radius', permissionreq: 'any', dataval: (d) => d }, + light_sensitivity_multiplier: { refersto: 'light_sensitivity_multiplier', permissionreq: 'any', dataval: (d) => d }, + light_sensitivity_mult: { refersto: 'light_sensitivity_multiplier', permissionreq: 'any', dataval: (d) => d }, + limit_field_of_night_vision_center: { refersto: 'limit_field_of_night_vision_center', permissionreq: 'any', dataval: (d) => d }, + limit_field_of_night_vision_total: { refersto: 'limit_field_of_night_vision_total', permissionreq: 'any', dataval: (d) => d }, + limit_field_of_vision_center: { refersto: 'limit_field_of_vision_center', permissionreq: 'any', dataval: (d) => d }, + limit_field_of_vision_total: { refersto: 'limit_field_of_vision_total', permissionreq: 'any', dataval: (d) => d }, + low_light_distance: { refersto: 'low_light_distance', permissionreq: 'any', dataval: (d) => d }, + token_name: { refersto: 'name', permissionreq: 'any', dataval: (d) => d }, + night_vision_distance: { refersto: 'night_vision_distance', permissionreq: 'any', dataval: (d) => d }, + nv_dist: { refersto: 'night_vision_distance', permissionreq: 'any', dataval: (d) => d }, + nv_distance: { refersto: 'night_vision_distance', permissionreq: 'any', dataval: (d) => d }, + night_vision_effect: { refersto: 'night_vision_effect', permissionreq: 'any', dataval: (d) => d }, + nv_effect: { refersto: 'night_vision_effect', permissionreq: 'any', dataval: (d) => d }, + night_vision_tint: { refersto: 'night_vision_tint', permissionreq: 'any', dataval: (d) => d }, + nv_tint: { refersto: 'night_vision_tint', permissionreq: 'any', dataval: (d) => d }, + playersedit_aura1: { refersto: 'playersedit_aura1', permissionreq: 'any', dataval: (d) => d }, + playersedit_aura2: { refersto: 'playersedit_aura2', permissionreq: 'any', dataval: (d) => d }, + playersedit_bar1: { refersto: 'playersedit_bar1', permissionreq: 'any', dataval: (d) => d }, + playersedit_bar2: { refersto: 'playersedit_bar2', permissionreq: 'any', dataval: (d) => d }, + playersedit_bar3: { refersto: 'playersedit_bar3', permissionreq: 'any', dataval: (d) => d }, + playersedit_name: { refersto: 'playersedit_name', permissionreq: 'any', dataval: (d) => d }, + represents: { refersto: 'represents', permissionreq: 'any', dataval: (d) => d }, + reps: { refersto: 'represents', permissionreq: 'any', dataval: (d) => d }, + represents_name: { refersto: 'represents', permissionreq: 'any', dataval: d => getObjName(d, 'character') }, + reps_name: { refersto: 'represents', permissionreq: 'any', dataval: d => getObjName(d, 'character') }, + rotation: { refersto: 'rotation', permissionreq: 'any', dataval: (d) => d }, + showname: { refersto: 'showname', permissionreq: 'any', dataval: (d) => d }, + showplayers_aura1: { refersto: 'showplayers_aura1', permissionreq: 'any', dataval: (d) => d }, + showplayers_aura2: { refersto: 'showplayers_aura2', permissionreq: 'any', dataval: (d) => d }, + showplayers_bar1: { refersto: 'showplayers_bar1', permissionreq: 'any', dataval: (d) => d }, + showplayers_bar2: { refersto: 'showplayers_bar2', permissionreq: 'any', dataval: (d) => d }, + showplayers_bar3: { refersto: 'showplayers_bar3', permissionreq: 'any', dataval: (d) => d }, + showplayers_name: { refersto: 'showplayers_name', permissionreq: 'any', dataval: (d) => d }, + sides: { refersto: 'sides', permissionreq: 'any', dataval: (d) => d }, + markers: { refersto: 'statusmarkers', permissionreq: 'any', dataval: (d) => d }, + statusmarkers: { refersto: 'statusmarkers', permissionreq: 'any', dataval: (d) => d }, + tint: { refersto: 'tint_color', permissionreq: 'any', dataval: (d) => d }, + tint_color: { refersto: 'tint_color', permissionreq: 'any', dataval: (d) => d }, + top: { refersto: 'top', permissionreq: 'any', dataval: (d) => d }, + width: { refersto: 'width', permissionreq: 'any', dataval: (d) => d } + }; + const charProps = { + char_id: { refersto: '_id', permissionsreq: 'any', dataval: (d) => d }, + character_id: { refersto: '_id', permissionsreq: 'any', dataval: (d) => d }, + character_type: { refersto: '_type', permissionsreq: 'any', dataval: (d) => d }, + char_type: { refersto: '_type', permissionsreq: 'any', dataval: (d) => d }, + avatar: { refersto: 'avatar', permissionsreq: 'any', dataval: (d) => d }, + char_name: { refersto: 'name', permissionsreq: 'any', dataval: (d) => d }, + character_name: { refersto: 'name', permissionsreq: 'any', dataval: (d) => d }, + archived: { refersto: 'archived', permissionsreq: 'any', dataval: (d) => d }, + inplayerjournals: { refersto: 'inplayerjournals', permissionsreq: 'any', dataval: (d) => d }, + character_controlledby: { refersto: 'controlledby', permissionsreq: 'any', dataval: (d) => d }, + character_cby: { refersto: 'controlledby', permissionsreq: 'any', dataval: (d) => d }, + char_cby: { refersto: 'controlledby', permissionsreq: 'any', dataval: (d) => d }, + char_controlledby: { refersto: 'controlledby', permissionsreq: 'any', dataval: (d) => d }, + character_controlledby_name: { refersto: 'controlledby', permissionsreq: 'any', dataval: (d) => getObjName(d, 'playerlist') }, + character_cby_name: { refersto: 'controlledby', permissionsreq: 'any', dataval: (d) => getObjName(d, 'playerlist') }, + char_cby_name: { refersto: 'controlledby', permissionsreq: 'any', dataval: (d) => getObjName(d, 'playerlist') }, + char_controlledby_name: { refersto: 'controlledby', permissionsreq: 'any', dataval: (d) => getObjName(d, 'playerlist') }, + character_controlledby_names: { refersto: 'controlledby', permissionsreq: 'any', dataval: (d) => getObjName(d, 'playerlist') }, + character_cby_names: { refersto: 'controlledby', permissionsreq: 'any', dataval: (d) => getObjName(d, 'playerlist') }, + char_cby_names: { refersto: 'controlledby', permissionsreq: 'any', dataval: (d) => getObjName(d, 'playerlist') }, + char_controlledby_names: { refersto: 'controlledby', permissionsreq: 'any', dataval: (d) => getObjName(d, 'playerlist') }, + defaulttoken: { refersto: '_defaulttoken', permissionsreq: 'any', dataval: (d) => d } + }; + + const condensereturn = (funcret, status, notes) => { + funcret.runloop = (status.includes('changed') || status.includes('unresolved')); + if (status.length) { + funcret.status = status.reduce((m, v) => { + switch (m) { + case 'unchanged': + m = v; + break; + case 'changed': + m = v === 'unresolved' ? v : m; + break; + case 'unresolved': + break; + } + return m; + }); + } + funcret.notes = notes.join('
'); + return funcret; + }; + + const tokenrx = /@\((?selected|(?:[^|.]+?))[|.](?[^\s[|.)]+?)(?:[|.](?[^\s.[|]+?)){0,1}(?:\[(?[^\]]*?)]){0,1}\s*\)/gi; + const sheetitemrx = /(?(?:@|%))\((?[^|.]+?)[|.](?[^\s.[|)]+?)(?:[|.](?[^\s.[)]+?)){0,1}(?:\[(?[^\]]*?)]){0,1}\s*\)/gi; + const rptgitemrx = /(?(?:\*))\((?[^|.]+?)[|.](?
[^\s.|]+?)[|.]\[\s*(?.+?)\s*]\s*[|.](?[^[\s).]+?)(?:[|.](?[^\s.[)]+?)){0,1}(?:\[(?[^\]]*?)]){0,1}\s*\)/gi; + const macrorx = /#\((?[^\s.[)]+?)(?:\[(?[^\]]*?)]){0,1}\s*\)/gi; + + const testConstructs = c => { + let result = tokenrx.test(c) || sheetitemrx.test(c) || rptgitemrx.test(c) || macrorx.test(c); + tokenrx.lastIndex = 0; + sheetitemrx.lastIndex = 0; + rptgitemrx.lastIndex = 0; + macrorx.lastIndex = 0; + return result; + }; + const simpleObj = (o) => JSON.parse(JSON.stringify(o)); + const handleInput = (msg, msgstate = {}) => { + let funcret = { runloop: false, status: 'unchanged', notes: '' }; + if (msg.type !== 'api' || !testConstructs(msg.content)) return funcret; + if (!Object.keys(msgstate).length && scriptisplugin) return funcret; + let status = []; + let notes = []; + + + while (tokenrx.test(msg.content) || sheetitemrx.test(msg.content) || rptgitemrx.test(msg.content) || macrorx.test(msg.content)) { + tokenrx.lastIndex = 0; + sheetitemrx.lastIndex = 0; + rptgitemrx.lastIndex = 0; + macrorx.lastIndex = 0; + + // TOKENS + msg.content = msg.content.replace(tokenrx, (m, token, item, valtype, def = '') => { + let source; + let sourcechar; + let retval; + switch (token.toLowerCase()) { + case 'selected': + if (!msg.selected) { + notes.push(`No token selected for ${m}. Using default value.`); + retval = def; + } else { + if (Object.keys(tokenProps).includes(item.toLowerCase())) { // selected + token property = return token property || default + source = simpleObj(getToken(msg.selected[0]._id) || {}); + retval = tokenProps[item.toLowerCase()].dataval(source[tokenProps[item.toLowerCase()].refersto]); + if (typeof retval === 'undefined') { + notes.push(`No token property found for ${m}. Using default value.`); + retval = def; + } + } else { // selected + character attribute = return character attribute info || default + sourcechar = getChar(msg.selected[0]._id, msg.playerid); + source = simpleObj(sourcechar || {}); + if (!Object.keys(source).length) { + notes.push(`No character found for ${m}. Using default value.`); + retval = def; + } else { + if (Object.keys(charProps).includes(item.toLowerCase())) { + retval = charProps[item.toLowerCase()].dataval(source[charProps[item.toLowerCase()].refersto]); + if (typeof retval === 'undefined') { + notes.push(`No character property found for ${m}. Using default value.`); + retval = def; + } + } else { + retval = getSheetItemVal({ groups: { type: '@', character: source._id, item: item, valtype: valtype } }, msg.playerid, sourcechar); + if (typeof retval === 'undefined') { + notes.push(`No attribute found for ${m}. Using default value.`); + retval = def; + } + } + } + } + } + break; + default: + source = simpleObj(getToken(token, getPageID(msg.playerid)) || {}); + if (!Object.keys(source).length && Object.keys(tokenProps).includes(item.toLowerCase())) { // no token found + token property = return default + notes.push(`No token property found for ${m}. Using default value.`); + retval = def; + } else if (Object.keys(source).length && Object.keys(tokenProps).includes(item.toLowerCase())) { // token + token property = return token property || default + retval = tokenProps[item.toLowerCase()].dataval(source[tokenProps[item.toLowerCase()].refersto]); + if (typeof retval === 'undefined') { + notes.push(`No token property found for ${m}. Using default value.`); + retval = def; + } + } else if (Object.keys(source).length && !Object.keys(tokenProps).includes(item.toLowerCase())) { // token + character attribute/property = return character attribute info || default + sourcechar = getChar(token, msg.playerid); + source = simpleObj(sourcechar || {}); + if (!Object.keys(source).length) { + notes.push(`No character found for ${m}. Using default value.`); + retval = def; + } else { + if (Object.keys(charProps).includes(item.toLowerCase())) { // token + character property + retval = charProps[item.toLowerCase()].dataval(source[charProps[item.toLowerCase()].refersto]); + if (typeof retval === 'undefined') { + notes.push(`No character property found for ${m}. Using default value.`); + retval = def; + } + } else { // token + character attribute + retval = getSheetItemVal({ groups: { type: '@', character: source._id, item: item, valtype: valtype } }, msg.playerid, sourcechar); + if (typeof retval === 'undefined') { + notes.push(`No attribute found for ${m}. Using default value.`); + retval = def; + } + } + } + } else { // not a token (character or non existent) = leave everything to be caught by the later rx statements + retval = m; + } + } + if (retval) status.push('changed'); + return retval; + }); + + // STANDARD SHEET ITEMS + msg.content = msg.content.replace(sheetitemrx, (m, type, character, item, valtype, def = '') => { + let retval; + if (character.toLowerCase() === 'speaker') character = msg.who; + let sourcechar = getChar(character, msg.playerid); + let source = simpleObj(sourcechar || {}); + if (!sourcechar) { + notes.push(`Unable to find character for ${m}. Using default value.`); + retval = def; + } else { + if (Object.keys(charProps).includes(item.toLowerCase())) { + retval = charProps[item.toLowerCase()].dataval(source[charProps[item.toLowerCase()].refersto]); + if (typeof retval === 'undefined') { + notes.push(`Unable to find character property for ${m}. Using default value.`); + retval = def; + } + } else { + retval = getSheetItemVal({ groups: { type: type, character: sourcechar.id, item: item, valtype: valtype } }, msg.playerid, sourcechar); + if (typeof retval === 'undefined') { + notes.push(`Unable to find ${type === '@' ? 'attribute' : 'ability'} for ${m}. Using default value.`); + retval = def; + } + } + } + if (retval) status.push('changed'); + return retval; + }); + + // REPEATING SHEET ITEMS + msg.content = msg.content.replace(rptgitemrx, (m, type, character, section, pattern, valuesuffix, valtype, def = '') => { + let retval; + let bsel = false; + let sourcechar; + switch (character.toLowerCase()) { + case 'selected': + if (!msg.selected) { + notes.push(`No token selected for ${m}. Using default value.`); + bsel = true; + retval = def; + } else { + sourcechar = getChar(msg.selected[0]._id, msg.playerid); + } + break; + case 'speaker': + sourcechar = getChar(msg.who, msg.playerid); + break; + default: + sourcechar = getChar(character, msg.playerid); + } + + if (!sourcechar) { + if (!bsel) notes.push(`Unable to find character for ${m}. Using default value.`); //track note only if we haven't already tracked no selected + retval = def; + } else { + retval = getSheetItemVal({ groups: { type: type, character: sourcechar.id, valtype: valtype, section: section, pattern: pattern, valuesuffix: valuesuffix } }, msg.playerid, sourcechar); + if (typeof retval === 'undefined') { + notes.push(`Unable to find repeating item for ${m}. Using default value.`); + retval = def; + } + } + if (retval) status.push('changed'); + return retval; + }); + + // MACROS + msg.content = msg.content.replace(macrorx, (m, item, def = '') => { + let retval = def; + let locobj = findObjs({ type: 'macro', name: item })[0]; + const validator = e => ['all', msg.playerid].includes(e); + if (!locobj || !(msg.playerid === locobj.get('_playerid') || locobj.get('visibleto').split(',').some(validator))) { + status.push('unresolved'); + notes.push(`Unable to find macro named ${item}. Using default value.`); + return retval; + } + retval = locobj.get('action') || ''; + status.push('changed'); + return retval; + }); + + } + return condensereturn(funcret, status, notes); + }; + + let scriptisplugin = false; + const fetch = (m, s) => handleInput(m, s); + on('chat:message', handleInput); + on('ready', () => { + versionInfo(); + logsig(); + scriptisplugin = (typeof ZeroFrame !== `undefined`); + if (typeof ZeroFrame !== 'undefined') { + ZeroFrame.RegisterMetaOp(fetch); + } + }); + return { + }; +})(); +{ try { throw new Error(''); } catch (e) { API_Meta.Fetch.lineCount = (parseInt(e.stack.split(/\n/)[1].replace(/^.*:(\d+):.*$/, '$1'), 10) - API_Meta.Fetch.offset); } } \ No newline at end of file diff --git a/Fetch/Fetch.js b/Fetch/Fetch.js index 7613d27323..559bc01cb7 100644 --- a/Fetch/Fetch.js +++ b/Fetch/Fetch.js @@ -3,8 +3,8 @@ Name : Fetch GitHub : https://github.com/TimRohr22/Cauldron/tree/master/Fetch Roll20 Contact : timmaugh -Version : 1.0.7 -Last Update : 6/28/2021 +Version : 1.0.8 +Last Update : 7/20/2021 ========================================================= */ var API_Meta = API_Meta || {}; @@ -15,10 +15,10 @@ API_Meta.Fetch = { offset: Number.MAX_SAFE_INTEGER, lineCount: -1 }; const Fetch = (() => { const apiproject = 'Fetch'; - const version = '1.0.7'; + const version = '1.0.8'; const schemaVersion = 0.1; API_Meta[apiproject].version = version; - const vd = new Date(1624887801910); + const vd = new Date(1626810300264); 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) { @@ -525,7 +525,7 @@ const Fetch = (() => { const handleInput = (msg, msgstate = {}) => { let funcret = { runloop: false, status: 'unchanged', notes: '' }; if (msg.type !== 'api' || !testConstructs(msg.content)) return funcret; - if (!msgstate && scriptisplugin) return funcret; + if (!Object.keys(msgstate).length && scriptisplugin) return funcret; let status = []; let notes = []; diff --git a/Fetch/script.json b/Fetch/script.json index 6cd5faacfd..94604deea5 100644 --- a/Fetch/script.json +++ b/Fetch/script.json @@ -1,7 +1,7 @@ { "name": "Fetch", "script": "Fetch.js", - "version": "1.0.7", + "version": "1.0.8", "description": "Fetch is a meta-script and part of the Meta-Toolbox. Fetch offers a unified syntax to expand the amount of things that can be retrieved with simple token or sheet calls. You can retrieve any token property, character property, sheet attribute, repeating attribute, ability, or macro with syntax that is intentionally very similar to Roll20's own syntax structures.\r\rToken property : @(selected.currentside)\rSheet Attribute: @(selected.Strength)\rSheet Attribute: @(Bob the Hirsute.Strength.max)\rRepeating Attr : *(Englebert Slaptiback.spells.[spell_name~Fireball prepared].spell_roll)\r\rIt also expands the source of the returned sheet item to include 'speaker'.\r\r@(speaker.Strength.max) ... and can return the rowID of a repeating attribute, the row number ($0), or the name of either brand of reference.\r\rNot only do these offer the advantage of not breaking the chat message if they don't exist (the way a standard token or sheet item call would), they also give you the ability to substitute in a default value should the one you are looking for not exist: \r\r@(The President of Burundi.Coffee[default value here]) \r\rFor more information, see the original API forum thread:\r\rhttps://app.roll20.net/forum/post/10005732/meta-script-fetch-retrieve-attributes-repeating-attributes-abilities-or-token-properties)\r\rOr read about the full set of meta-scripts available: \r\r[Meta Toolbox Forum Thread](https://app.roll20.net/forum/post/10005695/script-set-the-meta-toolbox)", "authors": "timmaugh", "roll20userid": "5962076", @@ -19,7 +19,8 @@ "previousversions": [ "1.0.4", "1.0.5", - "1.0.6" + "1.0.6", + "1.0.7" ] } \ No newline at end of file diff --git a/MathOps/1.0.4/MathOps.js b/MathOps/1.0.4/MathOps.js new file mode 100644 index 0000000000..186d297da8 --- /dev/null +++ b/MathOps/1.0.4/MathOps.js @@ -0,0 +1,316 @@ +/* +========================================================= +Name : MathOps +GitHub : https://github.com/TimRohr22/Cauldron/tree/master/MathOps +Roll20 Contact : timmaugh +Version : 1.0.4 +Last Update : 7/20/2021 +========================================================= +*/ +var API_Meta = API_Meta || {}; +API_Meta.MathOps = { offset: Number.MAX_SAFE_INTEGER, lineCount: -1 }; +{ + try { throw new Error(''); } catch (e) { API_Meta.MathOps.offset = (parseInt(e.stack.split(/\n/)[1].replace(/^.*:(\d+):.*$/, '$1'), 10) - (13)); } +} + +const MathOps = (() => { + const apiproject = 'MathOps'; + const version = '1.0.4'; + const schemaVersion = 0.1; + API_Meta[apiproject].version = version; + const vd = new Date(1626810300264); + 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 mathprocessor = (() => { + const tokenize = code => { + let results = []; + let tokenRegExp = /\s*([A-Za-z\s'"`]+|-?[0-9]+(\.[0-9]+)?|\S)\s*/g; + + let m; + while ((m = tokenRegExp.exec(code)) !== null) + results.push(m[1]); + return results; + }; + + const isNumber = token => { + return token !== undefined && token.match(/^-?[0-9]*.?[0-9]+$/) !== null; + }; + + const isName = token => { + return token !== undefined && token.match(/^[A-Za-z\s'"`]+$/) !== null; + }; + + const parse = o => { + let tokens = tokenize(o.code); + let position = 0; + const peek = () => { + return tokens[position]; + }; + const peek1 = () => { + if (position < tokens.length - 1) { + return tokens[position + 1]; + } + }; + + const consume = token => { + position++; + }; + + const parsePrimaryExpr = () => { + let t = peek(); + + if (isNumber(t)) { + consume(t); + return { type: "number", value: t }; + } else if (isName(t)) { + if (funcbank.hasOwnProperty(t.toLowerCase()) && peek1() === '(') { + let f = t.toLowerCase(); + let p = []; + consume(t); + consume('('); + while (peek() !== ')') { + if (peek() === ',') { + consume(','); + } else { + p.push(parseExpr()); + } + } + if (peek() !== ")") throw "Expected )"; + consume(')'); + return { type: 'func', func: f, params: p }; + } else { + consume(t); + return { type: "name", id: t }; + } + } else if (t === "(") { + consume(t); + let expr = parseExpr(); + if (peek() !== ")") throw "Expected )"; + consume(")"); + return expr; + } else { + throw "Expected a number, a variable, or parentheses"; + } + }; + + const parseMulExpr = () => { + let expr = parsePrimaryExpr(); + let t = peek(); + while (t === "*" || t === "/" || t === "%") { + consume(t); + let rhs = parsePrimaryExpr(); + expr = { type: t, left: expr, right: rhs }; + t = peek(); + } + return expr; + }; + const parseExpr = () => { + let expr = parseMulExpr(); + let t = peek(); + while (t === "+" || t === "-") { + consume(t); + let rhs = parseMulExpr(); + expr = { type: t, left: expr, right: rhs }; + t = peek(); + } + return expr; + }; + let result = parseExpr(); + if (position !== tokens.length) throw "Unexpected '" + peek() + "'"; + return result; + }; + const funcbank = { + abs: Math.abs, + min: Math.min, + max: Math.max, + acos: Math.acos, + acosh: Math.acosh, + asin: Math.asin, + asinh: Math.asinh, + atan: Math.atan, + atanh: Math.atanh, + atantwo: Math.atan2, + cbrt: Math.cbrt, + ceiling: Math.ceil, + cos: Math.cos, + cosh: Math.cosh, + exp: Math.exp, + expmone: Math.expm1, + floor: Math.floor, + hypot: Math.hypot, + log: Math.log, + logonep: Math.log1p, + logten: Math.log10, + logtwo: Math.log2, + pow: (v, e = 1) => Math.pow(v, e), + rand: Math.random, + randb: (v1, v2) => { return Math.random() * (Math.max(v1, v2) - Math.min(v1, v2) + 1) + Math.min(v1, v2) }, + randib: (v1, v2) => { + let min = Math.ceil(Math.min(v1, v2)); + let max = Math.floor(Math.max(v1, v2)); + return Math.floor(Math.random() * (max - min) + min); + }, + randa: (...v) => v[Math.floor(Math.random() * v.length)], + round: (v, d = 0) => Math.round(v * 10 ** d) / 10 ** d, + sin: Math.sin, + sinh: Math.sinh, + sqrt: Math.sqrt, + tan: Math.tan, + tanh: Math.tanh, + trunc: Math.trunc + }; + const knownbank = { + e: Math.E, + pi: Math.PI, + lntwo: Math.LN2, + lnten: Math.LN10, + logtwoe: Math.LOG2E, + logtene: Math.LOG10E + } + const isNum = (v) => +v === +v; + const typeprocessor = { + '-': (a, b) => { return isNum(a) && isNum(b) ? Number(a) - Number(b) : `${a}-${b}`; }, + '+': (a, b) => { return isNum(a) && isNum(b) ? Number(a) + Number(b) : `${a}+${b}`; }, + '/': (a, b) => { return isNum(a) && isNum(b) ? Number(a) / Number(b) : `${a}/${b}`; }, + '*': (a, b) => { return isNum(a) && isNum(b) ? Number(a) * Number(b) : `${a}*${b}`; }, + '%': (a, b) => { return isNum(a) && isNum(b) ? Number(a) % Number(b) : `${a}%${b}`; } + }; + const isString = (s) => 'string' === typeof s || s instanceof String; + const evalops = o => { + if (!o.code || !isString(o.code)) return; + o.known = o.known || {}; + Object.assign(o.known, knownbank); + try { + const getVal = t => { + switch (t.type) { + case 'number': + return t.value; + case 'name': + return o.known[t.id.trim()] || t.id; + case 'func': + return funcbank[t.func](...t.params.map(p => getVal(p))); + default: + return typeprocessor[t.type](getVal(t.left), getVal(t.right)); + } + }; + return getVal(parse(o)); + } catch (error) { + return { message: error }; + } + }; + return evalops; + })(); + const mathrx = /(\()?{&\s*math\s*([^}]+)\s*}((?<=\({&\s*math\s*([^}]+)\s*})\)|\1)/g; + + const testConstructs = c => { + let result = mathrx.test(c); + mathrx.lastIndex = 0; + return result; + }; + const handleInput = (msg, msgstate = {}) => { + let funcret = { runloop: false, status: 'unchanged', notes: '' }; + if (msg.type !== 'api' || !testConstructs(msg.content)) return funcret; + if (!Object.keys(msgstate).length && scriptisplugin) return funcret; + let status = []; + let notes = []; + msg.content = msg.content.replace(mathrx, (m, padding, g1) => { + g1 = g1.replace(/\$\[\[(\d+)]]/g, (m1, roll) => { + let rollval; + if (msg.parsedinline) { + rollval = msg.parsedinline[roll].value; + } else if (msg.inlinerolls && msg.inlinerolls[roll]) { + rollval = msg.inlinerolls[roll].results.total; + } else { + rollval = 0; + } + return rollval; + }); + let result = mathprocessor({ code: g1, known: msg.variables || {} }); + if (result.message) { // error + status.push('unresolved'); + notes.push(result.message); + return m; + } else { + status.push('changed'); + return result; + } + }); + funcret.runloop = (status.includes('changed') || status.includes('unresolved')); + funcret.status = status.reduce((m, v) => { + switch (m) { + case 'unchanged': + m = v; + break; + case 'changed': + m = v === 'unresolved' ? v : m; + break; + case 'unresolved': + break; + } + return m; + }); + funcret.notes = notes.join('
'); + return funcret; + }; + + let scriptisplugin = false; + const mathops = (m, s) => handleInput(m, s); + on('chat:message', handleInput); + on('ready', () => { + versionInfo(); + logsig(); + scriptisplugin = (typeof ZeroFrame !== `undefined`); + if (typeof ZeroFrame !== 'undefined') { + ZeroFrame.RegisterMetaOp(mathops, { priority: 55, handles: ['math'] }); + } + }); + return { + }; +})(); +{ try { throw new Error(''); } catch (e) { API_Meta.MathOps.lineCount = (parseInt(e.stack.split(/\n/)[1].replace(/^.*:(\d+):.*$/, '$1'), 10) - API_Meta.MathOps.offset); } } \ No newline at end of file diff --git a/MathOps/MathOps.js b/MathOps/MathOps.js index 5fe8496269..186d297da8 100644 --- a/MathOps/MathOps.js +++ b/MathOps/MathOps.js @@ -3,8 +3,8 @@ Name : MathOps GitHub : https://github.com/TimRohr22/Cauldron/tree/master/MathOps Roll20 Contact : timmaugh -Version : 1.0.3 -Last Update : 6/28/2021 +Version : 1.0.4 +Last Update : 7/20/2021 ========================================================= */ var API_Meta = API_Meta || {}; @@ -15,10 +15,10 @@ API_Meta.MathOps = { offset: Number.MAX_SAFE_INTEGER, lineCount: -1 }; const MathOps = (() => { const apiproject = 'MathOps'; - const version = '1.0.3'; + const version = '1.0.4'; const schemaVersion = 0.1; API_Meta[apiproject].version = version; - const vd = new Date(1624887801910); + const vd = new Date(1626810300264); 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) { @@ -256,7 +256,7 @@ const MathOps = (() => { const handleInput = (msg, msgstate = {}) => { let funcret = { runloop: false, status: 'unchanged', notes: '' }; if (msg.type !== 'api' || !testConstructs(msg.content)) return funcret; - if (!msgstate && scriptisplugin) return funcret; + if (!Object.keys(msgstate).length && scriptisplugin) return funcret; let status = []; let notes = []; msg.content = msg.content.replace(mathrx, (m, padding, g1) => { diff --git a/MathOps/script.json b/MathOps/script.json index 4931dc3145..ccde98490d 100644 --- a/MathOps/script.json +++ b/MathOps/script.json @@ -1,7 +1,7 @@ { "name": "MathOps", "script": "MathOps.js", - "version": "1.0.3", + "version": "1.0.4", "description": "MathOps is a meta-script and part of the Meta-Toolbox. MathOps offers a way to perform inline math operations in the macro command line for other scripts without the need for inline rolls and unpacking inline roll values. MathOps goes beyond standard inline rolls with the ability to nest operations and utilize built-in functions. Operations include addition (+), subtraction (-), multiplication (*), division (/), and modulo (%), and functions include min, max, absolute, and truncate, randomizer functions such as random, random integer, random among, and random between, trigonometric functions like sin, cos, tan, hypotenuse, arc- versions of the standard trig functions and hyperbolic versions, as well. It comes with built in constants, and can reference loaded variables if you have Muler (another meta-script) installed. Order operations with nested parentheses. \r\rFor more information, see the original thread in the API forum:\r\r[MathOps Forum Thread](https://app.roll20.net/forum/post/10005717/meta-script-mathops-inline-math-operations-and-nested-formulas)\r\rOr read about the full set of meta-scripts available: \r\r[Meta Toolbox Forum Thread](https://app.roll20.net/forum/post/10005695/script-set-the-meta-toolbox)", "authors": "timmaugh", "roll20userid": "5962076", @@ -10,5 +10,8 @@ "state.MathOps": "read, write" }, "conflicts": [], - "previousversions": ["1.0.2"] + "previousversions": [ + "1.0.2", + "1.0.3" + ] } \ No newline at end of file diff --git a/Muler/1.0.7/Muler.js b/Muler/1.0.7/Muler.js new file mode 100644 index 0000000000..a56682e6ec --- /dev/null +++ b/Muler/1.0.7/Muler.js @@ -0,0 +1,346 @@ +/* +========================================================= +Name : Muler +GitHub : https://github.com/TimRohr22/Cauldron/tree/master/Muler +Roll20 Contact : timmaugh +Version : 1.0.7 +Last Update : 7/20/2021 +========================================================= +*/ +var API_Meta = API_Meta || {}; +API_Meta.Muler = { offset: Number.MAX_SAFE_INTEGER, lineCount: -1 }; +{ + try { throw new Error(''); } catch (e) { API_Meta.Muler.offset = (parseInt(e.stack.split(/\n/)[1].replace(/^.*:(\d+):.*$/, '$1'), 10) - (13)); } +} + +const Muler = (() => { + const apiproject = 'Muler'; + const version = '1.0.7'; + const schemaVersion = 0.1; + API_Meta[apiproject].version = version; + const vd = new Date(1626810300264); + 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 escapeRegExp = (string) => { return string.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&'); }; + + const charFromAmbig = (query, pid) => { // find a character where info is an identifying piece of information (id, name, or token id) + let character; + let qrx = new RegExp(escapeRegExp(query), 'i'); + let charsIControl = findObjs({ type: 'character' }); + charsIControl = playerIsGM(pid) ? charsIControl : charsIControl.filter(c => c.get('controlledby').split(',').includes(pid)); + character = charsIControl.filter(c => c.id === query)[0] || + charsIControl.filter(c => c.id === (getObj('graphic', query) || { get: () => { return '' } }).get('represents'))[0] || + charsIControl.filter(c => c.get('name') === query)[0] || + charsIControl.filter(c => qrx.test(c)).reduce((m, v) => { + let d = getEditDistance(query, v); + return !m.length || d < m[1] ? [v, d] : m; + }, [])[0]; + return character; + }; + const getEditDistance = (a, b) => { + if (a.length === 0) return b.length; + if (b.length === 0) return a.length; + + var matrix = []; + + // increment along the first column of each row + var i; + for (i = 0; i <= b.length; i++) { + matrix[i] = [i]; + } + + // increment each column in the first row + var j; + for (j = 0; j <= a.length; j++) { + matrix[0][j] = j; + } + + // Fill in the rest of the matrix + for (i = 1; i <= b.length; i++) { + for (j = 1; j <= a.length; j++) { + if (b.charAt(i - 1) === a.charAt(j - 1)) { + matrix[i][j] = matrix[i - 1][j - 1]; + } else { + matrix[i][j] = Math.min(matrix[i - 1][j - 1] + 1, // substitution + Math.min(matrix[i][j - 1] + 1, // insertion + matrix[i - 1][j] + 1)); // deletion + } + } + } + + return matrix[b.length][a.length]; + }; + + const varrx = /^((?:(?:\d+)-(?:\d+)|(?:!=|>=|<=|>|<)(?:\d+))|[^\s]+?)=(.+)$/, + getrx = /get\.([^\s./]+(?:\.[^\s./]+?)*)(\/get|(?=\/|\s|$))/gmi, + setrx = /set\.([^\s.=]+(?:\.[^\s=.]+)*\s*=\s*.+?)\s*\/set/gmi, + mulerx = /(\()?{&\s*mule\s*(.*?)\s*}((?<=\({&\s*mule\s*(.*?)\s*})\)|\1)/gi, + muleabilrx = /\s*\((.*?)\)\s*/g; + + const condensereturn = (funcret, status, notes) => { + funcret.runloop = (status.includes('changed') || status.includes('unresolved')); + if (status.length) { + funcret.status = status.reduce((m, v) => { + switch (m) { + case 'unchanged': + m = v; + break; + case 'changed': + m = v === 'unresolved' ? v : m; + break; + case 'unresolved': + break; + } + return m; + }); + } + funcret.notes = notes.join('
'); + return funcret; + }; + const testGetConstructs = m => { + let result = mulerx.test(m.content) || (Object.keys(m.variables).length && getrx.test(m.content)); + getrx.lastIndex = 0; + mulerx.lastIndex = 0; + return result; + }; + const testSetConstructs = m => { + let result = Object.keys(m.variables).length && setrx.test(m.content); + setrx.lastIndex = 0; + return result; + }; + const internalTestLib = { + 'int': (v) => +v === +v && parseInt(parseFloat(v, 10), 10) == v, + 'num': (v) => +v === +v, + 'tru': (v) => v == true + }; + const mulegetter = (msg, msgstate = {}) => { + let funcret = { runloop: false, status: 'unchanged', notes: '' }; + msg.variables = msg.variables || {}; + msg.mules = msg.mules || []; + if (msg.type !== 'api' || !testGetConstructs(msg)) return funcret; + if (!Object.keys(msgstate).length && scriptisplugin) return funcret; + let status = []; + let notes = []; + + let variables = msg.variables; + if (mulerx.test(msg.content)) { + mulerx.lastIndex = 0; + let characters = findObjs({ type: 'character' }); + characters = playerIsGM(msg.playerid) ? characters : characters.filter(c => c.get('controlledby').split(',').includes(msg.playerid)); + + let mulearray = []; + msg.content = msg.content.replace(mulerx, (m, padding, g1) => { + g1 = g1.replace(muleabilrx, (m1, m1g1) => { + mulearray.push(m1g1); + return ''; + }); + g1.split(/\s+/).forEach(a => mulearray.push(a)); + status.push('changed'); + return ''; + }); + let charids = characters.map(c => c.id); + let vararray = []; + let mules = []; // new mules in this pass + mulearray.forEach(m => { + let mchar; + if (/\./.test(m)) { + mchar = charFromAmbig(m.slice(0, m.indexOf('.')), msg.playerid); + mchar = charids.includes((mchar || { id: undefined }).id) ? mchar : undefined; + } + if (mchar) { + mules.push(findObjs({ type: 'ability', name: m.slice(m.indexOf('.') + 1), characterid: mchar.id })[0]); + } else { + mules.push(findObjs({ type: 'ability', name: m }).filter(a => charids.includes(a.get('characterid')))[0]); + } + }); + + mules = mules.filter(a => a); + mules.forEach(a => { + msg.mules.push(a); + let achar = characters.filter(c => c.id === a.get('characterid'))[0]; + a.localaction = a.get('action'); + a.localaction + .split('\n') + .filter(v => varrx.test(v)) + .forEach(v => { + let k = varrx.exec(v); + vararray.push([k[1], k[2]]); + vararray.push([`${a.get('name')}.${k[1]}`, k[2]]); + vararray.push([`${achar.get('name')}.${a.get('name')}.${k[1]}`, k[2]]); + }); + Object.assign(variables, Object.fromEntries(vararray)); + }); + } + const typeProcessor = { + '!=': (r, t) => r != t, + '>': (r, t) => r > t, + '>=': (r, t) => r >= t, + '<': (r, t) => r < t, + '<=': (r, t) => r <= t, + '-': (r, l, h) => r >= l && r <= h, + }; + msg.content = msg.content.replace(getrx, (m, gvar,gclose) => { + let gchar, gmule; + let fullgvar, gvarheader = ''; + let dotcount = gvar.split('').filter(l => l === '.').length; + if (dotcount > 1) { + [gchar, gmule, ...gvar] = gvar.split('.'); + if (dotcount > 2) gvar = gvar.join('.'); + gchar = (charFromAmbig(gchar, msg.playerid) || { + get: () => { return gchar } + }).get('name'); + gvarheader = `${gchar}.${gmule}.`; + fullgvar = `${gchar}.${gmule}.${gvar}`; + } else { + fullgvar = gvar; + } + + let retval = variables[fullgvar]; + if (!retval && internalTestLib.num(gvar)) { // no explicit variable, but it's a number, so we check for a range variable + let varrangerx = /((?\d+)-(?\d+)|(?!=|>=|<=|>|<)(?\d+))$/; + let res; + let keys = Object.keys(variables) + .filter(k => k.startsWith(gvarheader)) + .filter(k => varrangerx.test(k)) + .filter(p => { + res = varrangerx.exec(p); + return res.groups.low ? + typeProcessor['-'](Number(gvar), Number(res.groups.low), Number(res.groups.high)) : + typeProcessor[res.groups.range](Number(gvar), Number(res.groups.singleval)); + }); + retval = variables[keys[0]]; + } + if (retval) { + status.push('changed'); + } else { + status.push('unresolved'); + notes.push(`Unable to resolve variable: get.${fullgvar}${gclose}`); + } + return retval || `get.${fullgvar}${gclose}`; // leaving the `get.` trusting ZeroFrame to stop infinite loops of processing + }); + return condensereturn(funcret, status, notes); + }; + const mulesetter = (msg, msgstate = {}) => { + let funcret = { runloop: false, status: 'unchanged', notes: '' }; + if (msg.type !== 'api' || !testSetConstructs(msg)) return funcret; + if (!Object.keys(msgstate).length && scriptisplugin) return funcret; + let status = []; + let notes = []; + let characters = findObjs({ type: 'character' }) + .filter(c => c.get('controlledby').split(',').includes(msg.playerid)); + let charids = characters.map(c => c.id); + + msg.content = msg.content.replace(setrx, (m, g1) => { + let setres = /\s*=\s*/.exec(g1); + let [svar, sval] = [g1.slice(0, setres.index), g1.slice(setres.index + setres[0].length)]; + let schar, smule; + let dotcount = svar.split('').filter(l => l === '.').length; + switch (dotcount) { + case 0: + break; + case 1: + [smule, svar] = svar.split('.'); + break; + default: + [schar, smule, ...svar] = svar.split('.'); + svar = svar.join('.'); + break; + } + + // write new value back to mule ability + let svarrx = new RegExp(`^${escapeRegExp(svar)}\\s*=.+$`, 'm'); + if (smule) { // mule declared, create if doesn't exist + if (!schar) { + schar = charFromAmbig(charids[0], msg.playerid); + smule = msg.mules.filter(m => m.get('name') === smule) || [createObj('ability', { characterid: schar.id, name: smule })]; + } else { + schar = charFromAmbig(schar, msg.playerid); + smule = msg.mules.filter(m => m.get('name') === smule && m.get('characterid') === schar.id) || [createObj('ability', { characterid: schar.id, name: smule })]; + } + } else { // no mule declared, so we have to find if the variable exists + smule = msg.mules.filter(m => svarrx.test(m.localaction)); + smule = smule.length ? smule : msg.mules; + } + let vararray = []; + smule.forEach(m => { + if (svarrx.test(m.localaction)) { // existing variable in known mule + m.localaction = m.localaction.replace(svarrx, `${svar}=${sval}`); + } else { // no text in the action, or it's missing this variable + m.localaction = `${m.localaction}\n${svar}=${sval}`; + } + m.set({ action: m.localaction }); + vararray.push([svar, sval]); + vararray.push([`${m.get('name')}.${svar}`, sval]); + vararray.push([`${charFromAmbig(m.get('characterid'), msg.playerid).get('name')}.${m.get('name')}.${svar}`, sval]); + }); + Object.assign(msg.variables, Object.fromEntries(vararray)); + status.push('changed'); + return ''; + }); + return condensereturn(funcret, status, notes); + }; + + let scriptisplugin = false; + const mulerget = (m, s) => mulegetter(m, s); + const mulerset = (m, s) => mulesetter(m, s); + on('chat:message', mulegetter); + on('chat:message', mulesetter); + on('ready', () => { + versionInfo(); + logsig(); + scriptisplugin = (typeof ZeroFrame !== `undefined`); + if (typeof ZeroFrame !== 'undefined') { + ZeroFrame.RegisterMetaOp(mulerget, { priority: 25, handles: ['get', 'muleget', 'muleload', 'load'] }); + ZeroFrame.RegisterMetaOp(mulerset, { priority: 65, handles: ['set', 'muleset'] }); + } + }); + return { + }; +})(); +{ try { throw new Error(''); } catch (e) { API_Meta.Muler.lineCount = (parseInt(e.stack.split(/\n/)[1].replace(/^.*:(\d+):.*$/, '$1'), 10) - API_Meta.Muler.offset); } } \ No newline at end of file diff --git a/Muler/Muler.js b/Muler/Muler.js index 1a9c628c00..a56682e6ec 100644 --- a/Muler/Muler.js +++ b/Muler/Muler.js @@ -3,8 +3,8 @@ Name : Muler GitHub : https://github.com/TimRohr22/Cauldron/tree/master/Muler Roll20 Contact : timmaugh -Version : 1.0.6 -Last Update : 6/28/2021 +Version : 1.0.7 +Last Update : 7/20/2021 ========================================================= */ var API_Meta = API_Meta || {}; @@ -15,10 +15,10 @@ API_Meta.Muler = { offset: Number.MAX_SAFE_INTEGER, lineCount: -1 }; const Muler = (() => { const apiproject = 'Muler'; - const version = '1.0.6'; + const version = '1.0.7'; const schemaVersion = 0.1; API_Meta[apiproject].version = version; - const vd = new Date(1624887801910); + const vd = new Date(1626810300264); 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) { @@ -164,7 +164,7 @@ const Muler = (() => { msg.variables = msg.variables || {}; msg.mules = msg.mules || []; if (msg.type !== 'api' || !testGetConstructs(msg)) return funcret; - if (!msgstate && scriptisplugin) return funcret; + if (!Object.keys(msgstate).length && scriptisplugin) return funcret; let status = []; let notes = []; @@ -269,7 +269,7 @@ const Muler = (() => { const mulesetter = (msg, msgstate = {}) => { let funcret = { runloop: false, status: 'unchanged', notes: '' }; if (msg.type !== 'api' || !testSetConstructs(msg)) return funcret; - if (!msgstate && scriptisplugin) return funcret; + if (!Object.keys(msgstate).length && scriptisplugin) return funcret; let status = []; let notes = []; let characters = findObjs({ type: 'character' }) diff --git a/Muler/script.json b/Muler/script.json index 2240963bb1..1cc87d1a01 100644 --- a/Muler/script.json +++ b/Muler/script.json @@ -1,7 +1,7 @@ { "name": "Muler", "script": "Muler.js", - "version": "1.0.6", + "version": "1.0.7", "description": "Muler is a meta-script and part of the Meta-Toolbox. Muler allows for you to designate and store variables in a character ability (called a Mule), then retrieve them in another call, and all from within other scripts.\t\tThese variables may be used as static-access tables, where you look up the value associated with a particular key, including keys that fall within given numerical ranges. \r\rFor more information, see the original thread in the API forum:\r\r[Muler Forum Thread](https://app.roll20.net/forum/post/10005710/meta-script-muler-get-slash-set-variables-static-tables/?pageforid=10005710#post-10005710)\r\rOr read about the full set of meta-scripts available: \r\r[Meta Toolbox Forum Thread](https://app.roll20.net/forum/post/10005695/script-set-the-meta-toolbox)", "authors": "timmaugh", "roll20userid": "5962076", @@ -13,6 +13,7 @@ "conflicts": [], "previousversions": [ "1.0.4", - "1.0.5" + "1.0.5", + "1.0.6" ] } diff --git a/Plugger/1.0.5/Plugger.js b/Plugger/1.0.5/Plugger.js new file mode 100644 index 0000000000..9917a56506 --- /dev/null +++ b/Plugger/1.0.5/Plugger.js @@ -0,0 +1,483 @@ +/* +========================================================= +Name : Plugger +GitHub : https://github.com/TimRohr22/Cauldron/tree/master/Plugger +Roll20 Contact : timmaugh +Version : 1.0.5 +Last Update : 7/20/2021 +========================================================= +*/ +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.5'; + const schemaVersion = 0.1; + API_Meta[apiproject].version = version; + const vd = new Date(1626810300264); + 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*(?