diff --git a/isBeacon/0.0.1/isBeacon.js b/isBeacon/0.0.1/isBeacon.js
new file mode 100644
index 000000000..62c4f08ae
--- /dev/null
+++ b/isBeacon/0.0.1/isBeacon.js
@@ -0,0 +1,154 @@
+/*
+=========================================================
+Name : isBeacon
+GitHub :
+Roll20 Contact : timmaugh
+Version : 0.0.1
+Last Update : 05 JUN 2025
+=========================================================
+*/
+var API_Meta = API_Meta || {};
+API_Meta.isBeacon = { offset: Number.MAX_SAFE_INTEGER, lineCount: -1 };
+{ try { throw new Error(''); } catch (e) { API_Meta.isBeacon.offset = (parseInt(e.stack.split(/\n/)[1].replace(/^.*:(\d+):.*$/, '$1'), 10) - (12)); } }
+const isBeacon = (() => {
+ const scriptName = "isBeacon";
+ const version = "0.0.1";
+ const lastUpdate = Date("June 5, 2025 10:00 ET");
+ const schemaVersion = 0.1;
+ const house = String.fromCodePoint(0x0001F3E0);
+
+ // ============================================
+ // STARTUP
+ // ============================================
+ const logsig = () => {
+ state.houseofmod = state.houseofmod || {};
+ state.houseofmod.siglogged = state.houseofmod.siglogged || false;
+ state.houseofmod.sigtime = state.houseofmod.sigtime || Date.now() - 3001;
+ if (
+ !state.houseofmod.siglogged ||
+ Date.now() - state.houseofmod.sigtime > 3000
+ ) {
+ const logsig =
+ "\n" +
+ " ______________________ \n" +
+ " ╱ ______ ╲ \n" +
+ " ╱ _____ ╲ \n" +
+ " ╱ ____ ╲ \n" +
+ " ╱_____________________________╲ \n" +
+ " | _____ _____ | \n" +
+ " | |_|_| MOD |_|_| | \n" +
+ " | |_|_| _____ |_|_| | \n" +
+ " | | | | \n" +
+ " | | o| | \n" +
+ "_____________|_________|___|________|______________\n" +
+ " \n";
+ log(`${logsig}`);
+ state.houseofmod.siglogged = true;
+ state.houseofmod.sigtime = Date.now();
+ }
+ return;
+ };
+
+ const checkInstall = () => {
+ log(`${house} ${scriptName} v${version} ${house} -- offset ${API_Meta[scriptName].offset}`);
+
+ if (!state.hasOwnProperty(scriptName) || state[scriptName].version !== schemaVersion) {
+ log(` > Updating Schema to v${schemaVersion} <`);
+ switch (state[scriptName] && state[scriptName].version) {
+ case 0.1:
+ /* falls through */
+ case "UpdateSchemaVersion":
+ state[scriptName].version = schemaVersion;
+ break;
+
+ default:
+ state[scriptName] = {
+ version: schemaVersion,
+ settings: {},
+ defaults: {}
+ };
+ break;
+ }
+ }
+ };
+
+ // ============================================
+ // DATA
+ // ============================================
+ let computeds = [];
+ let sheetObj = {};
+
+ const buildData = () => {
+ computeds = Object.keys(Campaign()?.computedSummary || {});
+
+ let sheetChars = findObjs({ type: "character" }).reduce((m, c) => ({ ...m, [c.get('charactersheetname')]: c.id }), {});
+
+ (async () => {
+ await Promise.all(Object.keys(sheetChars).map(async sheet => {
+ try {
+ sheetObj[sheet] = await Promise.any(computeds.map(async comp => {
+ if (comp) {
+ let v = await getComputed({ characterId: sheetChars[sheet], property: comp });
+ return typeof v !== 'undefined';
+ } else {
+ return false;
+ }
+ }));
+ } catch {
+ sheetObj[sheet] = false;
+ }
+ }));
+ })();
+ };
+ // ============================================
+ // TESTS
+ // ============================================
+ const getCharacter = (query) => {
+ let chars = findObjs({ type: 'character' });
+ return chars.filter(c => c.id === query)[0]
+ || chars.filter(c => c.id === (getObj('graphic', query) || { get: () => { return '' } }).get('represents'))[0]
+ || chars.filter(c => c.get('name') === query)[0];
+ };
+ const isBeaconTest = (query) => {
+ if (typeof 'query' !== 'string') {
+ return null;
+ }
+ if (sheetObj.hasOwnProperty(query)) {
+ return sheetObj[query];
+ } else {
+ let c = getCharacter(query);
+ if (c) {
+ return sheetObj[c.get('charactersheetname')] || null;
+ } else {
+ log(`isBeacon: No character or sheet data found.`);
+ return null;
+ }
+ }
+ };
+ // ============================================
+ // LISTENERS
+ // ============================================
+ const loadListeners = () => {
+ on('chat:message', msg => {
+ if (!msg.type === 'api' || !/^!getsheets/.test(msg.content)) { return; }
+ log(`${house} isBeacon Output ${house}`);
+ log(JSON.stringify(sheetObj, undefined, 2));
+ });
+ on('add:character', (c, p) => {
+ if (!sheetObj.hasOwnProperty(c.get('charactersheetname'))) {
+ buildData();
+ }
+ });
+ }
+ on('ready', () => {
+ logsig();
+ checkInstall();
+ buildData();
+ loadListeners();
+ });
+
+ return isBeaconTest;
+
+})();
+{ try { throw new Error(''); } catch (e) { API_Meta.isBeacon.lineCount = (parseInt(e.stack.split(/\n/)[1].replace(/^.*:(\d+):.*$/, '$1'), 10) - API_Meta.isBeacon.offset); } }
+/* */
\ No newline at end of file
diff --git a/isBeacon/isBeacon.js b/isBeacon/isBeacon.js
new file mode 100644
index 000000000..62c4f08ae
--- /dev/null
+++ b/isBeacon/isBeacon.js
@@ -0,0 +1,154 @@
+/*
+=========================================================
+Name : isBeacon
+GitHub :
+Roll20 Contact : timmaugh
+Version : 0.0.1
+Last Update : 05 JUN 2025
+=========================================================
+*/
+var API_Meta = API_Meta || {};
+API_Meta.isBeacon = { offset: Number.MAX_SAFE_INTEGER, lineCount: -1 };
+{ try { throw new Error(''); } catch (e) { API_Meta.isBeacon.offset = (parseInt(e.stack.split(/\n/)[1].replace(/^.*:(\d+):.*$/, '$1'), 10) - (12)); } }
+const isBeacon = (() => {
+ const scriptName = "isBeacon";
+ const version = "0.0.1";
+ const lastUpdate = Date("June 5, 2025 10:00 ET");
+ const schemaVersion = 0.1;
+ const house = String.fromCodePoint(0x0001F3E0);
+
+ // ============================================
+ // STARTUP
+ // ============================================
+ const logsig = () => {
+ state.houseofmod = state.houseofmod || {};
+ state.houseofmod.siglogged = state.houseofmod.siglogged || false;
+ state.houseofmod.sigtime = state.houseofmod.sigtime || Date.now() - 3001;
+ if (
+ !state.houseofmod.siglogged ||
+ Date.now() - state.houseofmod.sigtime > 3000
+ ) {
+ const logsig =
+ "\n" +
+ " ______________________ \n" +
+ " ╱ ______ ╲ \n" +
+ " ╱ _____ ╲ \n" +
+ " ╱ ____ ╲ \n" +
+ " ╱_____________________________╲ \n" +
+ " | _____ _____ | \n" +
+ " | |_|_| MOD |_|_| | \n" +
+ " | |_|_| _____ |_|_| | \n" +
+ " | | | | \n" +
+ " | | o| | \n" +
+ "_____________|_________|___|________|______________\n" +
+ " \n";
+ log(`${logsig}`);
+ state.houseofmod.siglogged = true;
+ state.houseofmod.sigtime = Date.now();
+ }
+ return;
+ };
+
+ const checkInstall = () => {
+ log(`${house} ${scriptName} v${version} ${house} -- offset ${API_Meta[scriptName].offset}`);
+
+ if (!state.hasOwnProperty(scriptName) || state[scriptName].version !== schemaVersion) {
+ log(` > Updating Schema to v${schemaVersion} <`);
+ switch (state[scriptName] && state[scriptName].version) {
+ case 0.1:
+ /* falls through */
+ case "UpdateSchemaVersion":
+ state[scriptName].version = schemaVersion;
+ break;
+
+ default:
+ state[scriptName] = {
+ version: schemaVersion,
+ settings: {},
+ defaults: {}
+ };
+ break;
+ }
+ }
+ };
+
+ // ============================================
+ // DATA
+ // ============================================
+ let computeds = [];
+ let sheetObj = {};
+
+ const buildData = () => {
+ computeds = Object.keys(Campaign()?.computedSummary || {});
+
+ let sheetChars = findObjs({ type: "character" }).reduce((m, c) => ({ ...m, [c.get('charactersheetname')]: c.id }), {});
+
+ (async () => {
+ await Promise.all(Object.keys(sheetChars).map(async sheet => {
+ try {
+ sheetObj[sheet] = await Promise.any(computeds.map(async comp => {
+ if (comp) {
+ let v = await getComputed({ characterId: sheetChars[sheet], property: comp });
+ return typeof v !== 'undefined';
+ } else {
+ return false;
+ }
+ }));
+ } catch {
+ sheetObj[sheet] = false;
+ }
+ }));
+ })();
+ };
+ // ============================================
+ // TESTS
+ // ============================================
+ const getCharacter = (query) => {
+ let chars = findObjs({ type: 'character' });
+ return chars.filter(c => c.id === query)[0]
+ || chars.filter(c => c.id === (getObj('graphic', query) || { get: () => { return '' } }).get('represents'))[0]
+ || chars.filter(c => c.get('name') === query)[0];
+ };
+ const isBeaconTest = (query) => {
+ if (typeof 'query' !== 'string') {
+ return null;
+ }
+ if (sheetObj.hasOwnProperty(query)) {
+ return sheetObj[query];
+ } else {
+ let c = getCharacter(query);
+ if (c) {
+ return sheetObj[c.get('charactersheetname')] || null;
+ } else {
+ log(`isBeacon: No character or sheet data found.`);
+ return null;
+ }
+ }
+ };
+ // ============================================
+ // LISTENERS
+ // ============================================
+ const loadListeners = () => {
+ on('chat:message', msg => {
+ if (!msg.type === 'api' || !/^!getsheets/.test(msg.content)) { return; }
+ log(`${house} isBeacon Output ${house}`);
+ log(JSON.stringify(sheetObj, undefined, 2));
+ });
+ on('add:character', (c, p) => {
+ if (!sheetObj.hasOwnProperty(c.get('charactersheetname'))) {
+ buildData();
+ }
+ });
+ }
+ on('ready', () => {
+ logsig();
+ checkInstall();
+ buildData();
+ loadListeners();
+ });
+
+ return isBeaconTest;
+
+})();
+{ try { throw new Error(''); } catch (e) { API_Meta.isBeacon.lineCount = (parseInt(e.stack.split(/\n/)[1].replace(/^.*:(\d+):.*$/, '$1'), 10) - API_Meta.isBeacon.offset); } }
+/* */
\ No newline at end of file
diff --git a/isBeacon/script.json b/isBeacon/script.json
new file mode 100644
index 000000000..9b95fdafe
--- /dev/null
+++ b/isBeacon/script.json
@@ -0,0 +1,13 @@
+{
+ "name": "isBeacon",
+ "script": "isBeacon.js",
+ "version": "0.0.1",
+ "description": "isBeacon provides a single point for detecting which sheet a character is using. The script exposes a single function (named isBeacon()
) and allows a scripter to pass a sheet name, a character name, a character ID, or the token ID of a token which represents a character. The function returns true
if the associated sheet is a Beacon sheet, or false
if it is legacy. This is for use by scripters as a tool they can leverage to include with their own scripts as a dependency.",
+ "authors": "timmaugh, The Aaron",
+ "roll20userid": "5962076, 104025",
+ "useroptions": [],
+ "dependencies": [],
+ "modifies": [],
+ "conflicts": [],
+ "previousversions": []
+}