Would look something like this completely untested script: on('ready',()=>{
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');
};
on('chat:message',(msg)=>{
if('api' !== msg.type ) {
return;
}
let args = msg.content.split(/\s+/);
const who = getObj('player',msg.playerid).get('_displayname');
switch(args.shift().toLowerCase()){
case '!cfx': {
let character = getObj('character',args[1]);
let destToken = getObj('graphic', args[2]);
if(args[0] && character && destToken) {
let pageid = getPageForPlayer(msg.playerid);
let tokens = findObjs({
represents: character.id,
pageid: pageid
});
if(tokens){
let destPt = {
x: destToken.get('left'),
y: destToken.get('top')
};
tokens.forEach((t)=>{
spawnFx(
{x:t.get('left'),y:t.get('top')},
destPt,
args[0],
pageid
);
});
} else {
sendChat('cfx',`/w "${who}" no tokens for ${character.get('name')} on your current page.`);
}
} else {
sendChat('cfx',`/w "${who}" use <code>!cfx [effect] [source character id] [destination token id]</code>.`);
}
}
break;
}
});
});
Which you'd call something like: !cfx beam-acid @{Bob The Slayer|character_id} @{target|Destination|token_id} Could be interesting for having a bunch of traps all breath fire on one guy too. =D