Try this: !aict +1 0 --Counts up by one !aict -1 10 --Counts down from 10 !aict -1 [[2d4+3]] --Rounds till Reinforcements Mostly place nice, though if you're using a script that changes the turn order (TurnMarker1, for example), the value won't be changed if that script changes the turn order to one of these turns. In practice, if you see one not changing, make sure the thing above it wasn't TurnMarker or some such. I'll try to fix that in the future... Here's the script: // Add Invisible CustomTurn
// By: The Aaron, Arcane Scriptomancer
// Contact: <a href="https://app.roll20.net/users/104025/the-aaron" rel="nofollow">https://app.roll20.net/users/104025/the-aaron</a>
var API_Meta = API_Meta||{}; //eslint-disable-line no-var
API_Meta.AddInvisibleCustomTurn={offset:Number.MAX_SAFE_INTEGER,lineCount:-1};
{try{throw new Error('');}catch(e){API_Meta.AddInvisibleCustomTurn.offset=(parseInt(e.stack.split(/\n/)[1].replace(/^.*:(\d+):.*$/,'$1'),10)-6);}}
on('ready',() => {
const version = '0.1.2';
API_Meta.AddInvisibleCustomTurn.version = version;
const aictImgSrc = "<a href="https://s3.amazonaws.com/files.d20.io/images/58732795/pfn3AoNw630KlzHP0dGMWw/thumb.png?1532188342" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/58732795/pfn3AoNw630KlzHP0dGMWw/thumb.png?1532188342</a>";
const activeAICTs = findObjs({type:'graphic',layer:'gmlayer'})
.filter((t)=>/images\/58732795\/pfn3AoNw630KlzHP0dGMWw\//.test(t.get('imgsrc')))
.filter((()=>{
const toIds = JSON.parse(Campaign().get('turnorder')||'[]').map(to=>to.id);
return t => {
if( ! toIds.includes(t.id)){
t.remove();
return false;
}
return true;
};
})())
.map((t)=>t.id)
;
const getPageForPlayer = (playerid) => {
let player = getObj('player',playerid);
if(playerIsGM(playerid)){
return player.get('lastpage');
}
let psp = Campaign().get('playerspecificpages');
if(psp[playerid]){
return psp[playerid];
}
return Campaign().get('playerpageid');
};
let lastTurnCache = '';
const checkFormulaOnTurn = _.debounce(() => {
let to=JSON.parse(Campaign().get('turnorder')||'[]');
if(to.length){
if(to[0].id!==lastTurnCache && activeAICTs.includes(to[0].id)) {
sendChat('',`[[${to[0].pr}+(${to[0].formula||0})]]`,(r)=>{
to[0].pr=r[0].inlinerolls[0].results.total;
Campaign().set('turnorder',JSON.stringify(to));
});
}
lastTurnCache = to[0].id;
}
},10);
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;
}
};
on('chat:message',function(msg){
if('api' === msg.type) {
if(msg.content.match(/^!aict\b/) ){
let who=(getObj('player',msg.playerid)||{get:()=>'API'}).get('_displayname');
let content = processInlinerolls(msg);
let args = content
.replace(/<br\/>\n/g, ' ')
.replace(/(\{\{(.*?)\}\})/g," $2 ")
.split(/\s+--/);
let cmds = args.shift().split(/\s+/);
let change = parseFloat(cmds[1]);
change = ( isNaN(change) ? '+1': change );
change = `${/^[+-]\d/.test(change)?'':'+'}${change}`;
let initial = parseFloat(cmds[2])||0;
let entry = args.join(' ');
if(entry.length){
let to=JSON.parse(Campaign().get('turnorder')||'[]');
let pageid = getPageForPlayer(msg.playerid);
let token = createObj('graphic', {
subtype: 'token',
imgsrc: aictImgSrc,
pageid: pageid,
layer: 'gmlayer',
top: -100,
left: -100,
width: 70,
height: 70,
name: entry,
showname: true
});
activeAICTs.push(token.id);
to.unshift({
id: token.id,
pr: initial,
_pageid: pageid,
formula: change
});
Campaign().set('turnorder',JSON.stringify(to));
if(!playerIsGM(msg.playerid)){
sendChat('AICT',`/w gm <div style="padding:1px 3px;border: 1px solid #8B4513;background: #eeffee; color: #8B4513; font-size: 80%;"><div style="background-color: #ffeeee;"><b>${who}</b> added entry for <b>${entry}</b> starting at <b>${initial}</b> and changing by <b>${change}</b>.</div></div>`);
}
} else {
sendChat('AICT',`/w "${who}" <div style="padding:1px 3px;border: 1px solid #8B4513;background: #eeffee; color: #8B4513; font-size: 80%;"><div style="background-color: #ffeeee;">Use <b><pre>!aict [formula] [starting value] --[description]</pre></b></div></div>`);
}
} else if(msg.content.match(/^!eot/i)){
_.defer(checkFormulaOnTurn);
}
}
});
on('change:campaign:turnorder',()=>{
_.defer(checkFormulaOnTurn);
});
});
{try{throw new Error('');}catch(e){API_Meta.AddInvisibleCustomTurn.lineCount=(parseInt(e.stack.split(/\n/)[1].replace(/^.*:(\d+):.*$/,'$1'),10)-API_Meta.AddInvisibleCustomTurn.offset);}}
Edit 2023-10-21: Updated to work with the changes to TurnOrder