The API can likely approximate what you're asking. Certainly it can: Change the controlled by property of a Token. Take actions based on the Turn Order Changing. Display things in the chat. If I were writing this, I'd have a "Rotate Players" entry in the Turn Order... Ok, never mind, I'd do this: For the characters that are to be shared, set their In Player Journals field to the people that should be cycled for control. Set the Controlledby to just the player that should be initially controlling them. On the token for the character, add [SHARED] in the GM Notes and set it back as the default token. This script will add an entry named "Rotate Players" to the end of the turn (put it wherever you like except first). When its turn comes up, it will cycle the players in the Controlledby field to the next one in the In Player Journals box, announce to chat, and then skip to the next turn. Script: on('ready',()=>{
const CustomTurnName = 'Rotate Players';
const CustomTurnPR = -1;
const parseTurnToArray = (orderStr) => ( '' === orderStr ? [] : JSON.parse(orderStr));
const getTurnArray = () => ( '' === Campaign().get('turnorder') ? [] : JSON.parse(Campaign().get('turnorder')));
const setTurnArray = (ta) => Campaign().set({turnorder: JSON.stringify(ta)});
const addCustomTurn = (custom, pr) => Campaign().set({ turnorder: JSON.stringify( [...getTurnArray(), {id:"-1",custom,pr}]) });
const assureTurnOrder = () => {
if(Campaign().get('initiativepage')){
// get tyo
let to = getTurnArray();
let idx = to.findIndex(o=>CustomTurnName === o.custom);
if(idx === -1){
addCustomTurn(CustomTurnName, CustomTurnPR);
}
}
};
const cyclePlayer = (c) => {
let ids = c.get('inplayerjournals').split(/\s*,\s*/);
let cid = c.get('controlledby').split(/\s*,\s*/)[0];
let idx = ids.findIndex(o=>cid===o)+1;
cid = ids[(ids.length === idx) ? 0 : idx];
c.set({controlledby: cid});
let p = getObj('player',cid);
return `<b>${c.get('name')}</b> is now controlled by <b>${p.get('displayname')}</b>.`;
};
const handleTurnOrderChange = (obj) => {
let to = parseTurnToArray(obj.get('turnorder'));
let idx = to.findIndex(o=>CustomTurnName === o.custom);
if(0===idx){
// get token ids
let messages = to.map(o=>getObj('graphic',o.id))
.filter(g=>undefined !== g)
.filter(t=>/\[shared\]/i.test(unescape(t.get('gmnotes'))))
.map(t=>getObj('character',t.get('represents')))
.filter(c=>undefined !== c)
.map(cyclePlayer);
if(messages.length){
sendChat('',`<div><ul>${messages.map(m=>`<li>${m}</li>`).join('')}</ul></div>`);
}
let e = to.shift();
to.push(e);
setTurnArray(to);
}
};
on('chat:message',(msg)=>{
if('api' === msg.type && /^!eot(\b\s|$)/i.test(msg.content)){
setTimeout(()=>handleTurnOrderChange(Campaign()),0);
}
});
on('change:campaign:initiativepage',assureTurnOrder);
on('change:campaign:turnorder',handleTurnOrderChange);
assureTurnOrder();
});
Let me know if you have any issues.