Skip to content

Instantly share code, notes, and snippets.

@stephaned68
Last active September 8, 2020 18:32
Show Gist options
  • Save stephaned68/6cda403867a49619c2a9864d7e7b9223 to your computer and use it in GitHub Desktop.
Save stephaned68/6cda403867a49619c2a9864d7e7b9223 to your computer and use it in GitHub Desktop.
A Roll20 API script to convert the page and the lighting settings of all NPC tokens from imperial to metric system
/**
* Syntax: !metric
* This script loops through all tokens in the current page
* Any token attached to a character has its lighting properties converted from feet to meters
* The page settings are also converted to using 'm' instead of 'ft'
* This script must be run by the GM of the campaign
* @version : 1.0.0
*/
on('chat:message', function (msg) {
const [cmd, ...args] = msg.content.split(/\s+/);
const playerId = msg.playerid;
// check entered command
if (msg.type == 'api' && cmd.indexOf('!metric') === 0) {
// make sure we are GM
if (!playerIsGM(playerId)) {
sendChat('API', 'Must be run as GM');
return;
}
// retrieve GM player object
const player = findObjs({
_type: 'player',
_id: playerId,
});
if (player.length !== 1) {
sendChat('API', 'Cound not find current player info');
return;
}
// retrieve current page GM is on
const pages = findObjs({
_type: 'page',
_id: player[0].get('_lastpage'),
});
if (pages.length !== 1) {
sendChat('API', 'Cound not find current page for GM');
return;
}
// check if dynamic lighting is on
const page = pages[0];
if (!page.get('dynamic_lighting_enabled')) {
sendChat('API', 'Dynamic lighting is not enabled on this page');
return;
}
// check args
let tokens = [];
if (args.indexOf('--selected') !== -1) {
if (msg.selected.length === 0) {
sendChat('API', 'You must select one or more token(s) to convert');
return;
} else {
msg.selected.forEach((item) => {
tokens.push(getObj(item._type, item._id));
});
}
}
// check current UoM
if (page.get('scale_units') === 'm' && tokens.length === 0) {
sendChat('API', 'Page already set to "m"eters.');
return;
}
// run the conversion process
sendChat('API', `Starting conversion of page ${page.get('name')}`);
convertToMetric(page, tokens);
}
});
/**
* Convert scale in page settings
* @param {object} page Roll20 API page object
*/
function convertPage(page) {
let scale_number = parseInt(page.get('scale_number')) || 0;
let scale_metric = 0;
switch (scale_number) {
case 5:
scale_metric = 1.5;
break;
case 10:
scale_metric = 3;
break;
case 15:
scale_metric = 4.5;
break;
case 20:
scale_metric = 6;
break;
default:
scale_metric = scale_number;
break;
}
page.set({
scale_units: 'm',
scale_number: scale_metric,
});
sendChat('API', `/w gm Page ${page.get('name')} converted to metric system.`);
return;
}
/**
* Convert a list of token properties from feet to meters
* @param {object} token Roll20 graphics/token object
* @param {array} props List of object properties to convert
*/
function convert(token, props) {
const result = {};
for (prop of props) {
value = parseInt(token.get(prop)) || 0;
if (value !== 0) {
result[prop] = Math.floor(value / 3.28);
}
}
return result;
}
/**
* Convert scale property values from feet to meters for all token on a page
* @param {string} page Roll20 API Page object
* @param {array<object>} selected List of currently selected token objects, if any
*/
function convertToMetric(page, selected) {
let tokens = selected || [];
// get all tokens on the page
if (tokens.length === 0) {
tokens = findObjs({
_type: 'graphic',
_subtype: 'token',
_pageid: page.get('_id'),
});
}
// async conversion worker
const convertNextToken = () => {
if (tokens.length > 0) {
token = tokens.shift();
const charId = token.get('represents');
if (charId !== '') {
// get the linked character object
const char = findObjs({
_type: 'character',
_id: charId,
});
if (char.length === 1) {
// convert the measures to metric
const converted = convert(token, [
'light_radius',
'light_dimradius',
'night_vision_distance',
'bright_light_distance',
'low_light_distance',
]);
token.set(converted);
sendChat('API', `${char[0].get('name')} ...converted`);
}
}
setTimeout(convertNextToken, 0);
} else {
convertPage(page);
}
};
convertNextToken();
/*
for (const token of tokens) {
// filter tokens with a linked character
const charId = token.get('represents');
if (charId !== '') {
// get the linked character object
const char = findObjs({
_type: 'character',
_id: charId,
});
if (char.length === 1) {
// convert the measures to metric
const converted = convert(token, [
'light_radius',
'light_dimradius',
'night_vision_distance',
'bright_light_distance',
'low_light_distance',
]);
token.set(converted);
sendChat('API', `${char[0].get('name')} ...converted`);
}
}
}
*/
}
@stephaned68
Copy link
Author

stephaned68 commented Sep 8, 2020

2020-09-08 -- Version 1.0.0

  • Code refactored to run conversion asynchronously, one token at a time, then convert page settings at the end
  • Added logic to allow only a limited set of tokens to be converted (they need to be selected on the tabletop and the script must be invoked with the --selected argument)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment