OK, here's my FlyMore script. Edit the statuses at the top to whatever you like. Note: you can have the groundStatus and upStatus set to the same thing with no problems. Call it with !fly: !fly 25 !fly [NUMBER] with tokens selected. NUMBER can be prefaced with an operation from among: + , - , * , / , or = . Prefacing with an operation will make a relative change, for example, !fly -20 will reduce the selected token elevation by 20. Note: to explicitly set a negative elevation, preface with = , for example, !fly =-20 will set the elevation to -20 no matter where it is currently. Code: on('ready', () => {
const upStatus = 'green';
const groundStatus = 'blue';
const downStatus = 'red';
const decomposeStatuses = (statuses) =>
statuses.split(/,/).reduce((memo,st,idx) => {
let parts=st.split(/@/);
let entry = {
mark: parts[0],
num: parseInt(parts[1],10),
idx: idx
};
if(isNaN(entry.num)){
entry.num='';
}
if(entry.mark.length) {
memo[entry.mark] = ( memo[entry.mark] && memo[entry.mark].push(entry) && memo[entry.mark]) || [entry] ;
}
return memo;
},{});
const composeStatuses = (statuses) =>
Object.keys(statuses).reduce((m,k) => {
statuses[k].forEach((sd)=>m.push(sd));
return m;
},[])
.sort((a,b)=>a.idx-b.idx)
.map( (s) => ('dead'===s.mark ? 'dead' : ( s.mark+(s.num!=='' ? '@'+s.num : ''))) )
.join(',');
const statusSetter = (op) => {
let applyOp = (e) => {
let n = Math.round(op(e));
if(0 !== n) {
return {
[n>0 ? upStatus : downStatus]:
n.toString()
.split('')
.filter(c=>/\d/.test(c))
.map((c,i) => ({
mark: (n>0 ? upStatus : downStatus),
num: c,
idx: 10000+i
}))
};
}
return {[groundStatus]:[{mark:groundStatus,num:'',idx:10000}]};
};
const clean = (sm) => {
delete sm[downStatus];
delete sm[groundStatus];
delete sm[upStatus];
return sm;
};
const getElevation = (sm) => {
let accum=0;
let marks = [upStatus,downStatus,groundStatus];
let mark = marks.find(s=>sm.hasOwnProperty(s));
let dir = (mark === downStatus) ? -1 : 1;
if(mark){
sm[mark].find(e=>{
if(Number.isInteger(e.num)){
accum*=10;
accum+=e.num;
return false;
}
return true;
});
}
return dir*accum;
};
const set = (sm,e) => ({...sm,...applyOp(e)});
return (t) => {
let sm = decomposeStatuses(t.get('statusmarkers'));
let e = getElevation(sm);
t.set({
statusmarkers: composeStatuses(set(clean(sm),e))
});
};
};
const processInlinerolls = (msg) => {
if(msg.hasOwnProperty('inlinerolls')){
return msg.inlinerolls
.reduce((m,v,k) => {
let ti=v.results.rolls.reduce((m2,v2) => {
if(v2.hasOwnProperty('table')){
m2.push(v2.results.reduce((m3,v3) => [...m3,v3.tableItem.name],[]).join(", "));
}
return m2;
},[]).join(', ');
return [...m,{k:`$[[${k}]]`, v:(ti.length && ti) || v.results.total || 0}];
},[])
.reduce((m,o) => m.replace(o.k,o.v), msg.content);
} else {
return msg.content;
}
};
const getChangeOp = (change) => {
const ops = {
'+': (a,b)=>a+b,
'-': (a,b)=>a-b,
'/': (a,b)=>a/(b||1),
'*': (a,b)=>a*b,
'!': (a,b)=>(a?0:b),
'=': (a,b)=>b
};
let txt = `${change}`;
let op = (ops.hasOwnProperty(txt[0]) ? txt[0] : "=");
let num = parseInt(txt.slice((txt[0]===op) ? 1 : 0));
return (n)=>ops[op](n,num);
};
on('chat:message',msg=>{
if('api'===msg.type && /^!fly(\b\s|$)/i.test(msg.content) ){
let who = (getObj('player',msg.playerid)||{get:()=>'API'}).get('_displayname');
let args = processInlinerolls(msg).split(/\s+/);
if(args.length < 2) {
sendChat('fly', `/w "${who}" <div><b>Usage:</b> <code>!fly [NUMBER]</code> with tokens selected. <code>NUMBER</code> can be prefaced with an operation from among: <code>+</code>, <code>-</code>, <code>*</code>, <code>/</code>, or <code>=</code>. Prefacing with an operation will make a relative change, for example, <code>!fly -20</code> will reduce the selected token elevation by 20. Note: to explicitly set a negative elevation, preface with <code>=</code>, for example, <code>!fly =-20</code> will set the elevation to -20 no matter where it is currently.</div>`);
return;
}
let op = getChangeOp(args[1]);
let tokens = (msg.selected || [])
.map(o => getObj('graphic',o._id))
.filter(t => undefined !== t)
.filter(t => t.get('subtype') === 'token')
;
let ss = statusSetter(op);
tokens.map(ss);
}
});
});