Upgraded and bug fixed version. There was a crash bug where if you didn't have enough patrons for the number of seats, it would try to dereference and undefined value, fixed. Additionally, since the ordering of tokens returned from findObj() is deterministic, it was always filling seats in the same order, it now randomizes which seat is attempted to fill next. Finally, I added a -- argument to tell it to seat in a different list: !take-a-seat --staff|cook That will find all the markers on the current page gm layer with the name "staff" (case insensitive) and randomly assign tokens with "cook" in their bar1 (case insensitive) in them. If you omit the |cook part, it will try to find tokens with the same value in their bar1 as the place you want to stick them, for example: !take-a-seat --staff is equivalent to: !take-a-seat --staff|staff You can still specify a likelihood that any given seat is filled: !take-a-seat 53 --staff You can use the same token on the GM layer, it doesn't matter. I just tinted them differently for contrast: Code: on('ready', ()=>{
const PATRON_LOCATION = 'bar1_value';
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');
};
const processInlinerolls = (msg) => {
if(_.has(msg,'inlinerolls')){
return _.chain(msg.inlinerolls)
.reduce(function(m,v,k){
let ti=_.reduce(v.results.rolls,function(m2,v2){
if(_.has(v2,'table')){
m2.push(_.reduce(v2.results,function(m3,v3){
m3.push(v3.tableItem.name);
return m3;
},[]).join(', '));
}
return m2;
},[]).join(', ');
m['$[['+k+']]']= (ti.length && ti) || v.results.total || 0;
return m;
},{})
.reduce(function(m,v,k){
return m.replace(k,v);
},msg.content)
.value();
} else {
return msg.content;
}
};
const esRE = (s) => s.replace(/(\\|\/|\[|\]|\(|\)|\{|\}|\?|\+|\*|\||\.|\^|\$)/g,'\\$1');
const matcher = (t) => new RegExp(`^\\s*${esRE(t)}\\s*$`,'i');
on('chat:message', (msg)=>{
if('api' === msg.type && /^!take-a-seat\b/i.test(msg.content) && playerIsGM(msg.playerid)){
let pageid = getPageForPlayer(msg.playerid);
let content = processInlinerolls(msg);
let where = 'seat';
let whom = 'patron';
let which = content.split(/\s+--/).slice(1);
let args = content.split(/\s+/);
let fill = parseInt(args[1],10)||75;
if(which.length){
let w = which.shift().split(/\|/);
where = w[0];
whom = w[1]||where;
}
let whereM = matcher(where);
let whomM = matcher(whom);
let seats = findObjs({
type: 'graphic',
layer: 'gmlayer',
pageid: pageid
}).filter((o)=>whereM.test(o.get('name')));
let patrons = findObjs({
type: 'graphic',
pageid: pageid
}).filter((o)=>whomM.test(o.get(PATRON_LOCATION)));
patrons.forEach((o)=>{
o.set({layer: 'gmlayer'});
});
const getRandomPatron = ()=>{
let e = randomInteger(patrons.length)-1;
let p = patrons[e];
patrons = [...patrons.slice(0,e),...patrons.slice(e+1)];
return p;
};
const getRandomSeat = ()=>{
let e = randomInteger(seats.length)-1;
let p = seats[e];
seats = [...seats.slice(0,e),...seats.slice(e+1)];
return p;
};
sendChat('TakeASeat',`/w gm Filling ${seats.length} seat(s) with a ${fill}% chance.`);
let s;
while(undefined != (s = getRandomSeat())){
if(randomInteger(100) <= fill){
let p = getRandomPatron();
if(p){
p.set({
layer: 'objects',
top: s.get('top'),
left: s.get('left'),
rotation: s.get('rotation')
});
}
}
}
let pos = {
top: 35,
left: 35
};
patrons.forEach(p=>{
p.set(pos);
pos.left+=70;
});
}
});
});