// Github: https://github.com/shdwjk/Roll20API/blob/master/TokenLock/TokenLock.js
// By: The Aaron, Arcane Scriptomancer
// Contact: https://app.roll20.net/users/104025/the-aaron
const TokenLock = (() => { // eslint-disable-line no-unused-vars
const version = '0.2.7';
const lastUpdate = 1566254659;
const schemaVersion = 0.2;
const ch = (c) => {
const entities = {
'<' : 'lt',
'>' : 'gt',
"'" : '#39',
'@' : '#64',
'{' : '#123',
'|' : '#124',
'}' : '#125',
'[' : '#91',
']' : '#93',
'"' : 'quot',
'*' : 'ast',
'/' : 'sol',
' ' : 'nbsp'
};
if( entities.hasOwnProperty(c) ){
return `&${entities[c]};`;
}
return '';
};
const getCommandOption_ToggleLock = () => {
const text = (state.TokenLock.locked ? 'Locked' : 'Unlocked' );
return '
';
};
const getConfigOption_AllowMoveOnTurn = () => {
const text = (state.TokenLock.config.allowMoveOnTurn ? 'On' : 'Off' );
return '';
};
const showHelp = (who) => {
const stateColor = (state.TokenLock.locked) ? ('#990000') : ('#009900');
const stateName = (state.TokenLock.locked) ? ('Locked') : ('Unlocked');
sendChat('',
'/w "'+who+'" '+
''+
'
'+
'
'+stateName+'
'+
'TokenLock v'+version+
'
'+
'
'+
'
'+
'
TokenLock allows the GM to selectively prevent players from moving their tokens. '+
'Since change:graphic events to not specify who changed the '+
'graphic, determination of player tokens is based on whether that token '+
'has an entry in the controlled by field of either the token or '+
'the character it represents. If controlled by is empty, the '+
'GM can freely move the token at any point. If there is any entry in '+
'controlled by, the token can only be moved when TokenLock is '+
'unlocked.
'+
'
Moving of player controlled cards is still permissible.
'+
'
'+
'
Commands'+
'
!tl'+
'
'+
'Executing the command with no arguments prints this help. The following arguments may be supplied in order to change the configuration. All changes are persisted between script restarts.'+
'
'+
'- '+
'lock -- Locks the player tokens to prevent moving them.'+
'
'+
'- '+
'unlock -- Unlocks the player tokens allowing them to be moved.'+
'
'+
'
'+
'
'+
'
'+
getCommandOption_ToggleLock()+
'
'+
'
!tl-config ['+ch('<')+'Options'+ch('>')+'|--help]'+
'
'+
'
Swaps the selected Tokens for their counterparts on the other layer.
'+
'
'+
'- '+
'--help '+ch('-')+' Shows the Help screen'+
'
'+
'- '+
'--toggle-allowmoveonturn '+ch('-')+' Sets whether tokens can be moved if they are at the top of the turn order.'+
'
'+
'
'+
'
'+
'
'+
getConfigOption_AllowMoveOnTurn()+
'
'
);
};
const handleInput = (msg) => {
if (msg.type !== "api" || !(playerIsGM(msg.playerid) || msg.playerid==='API') ) {
return;
}
let who = (getObj('player',msg.playerid)||{get:()=>'API'}).get('_displayname');
let args = msg.content.split(/\s+/);
switch(args.shift()) {
case '!tl':
if(args.includes('--help')) {
showHelp(who);
return;
}
switch(args.shift()) {
case 'lock':
state.TokenLock.locked=true;
sendChat('TokenLock','/w gm ' +
getCommandOption_ToggleLock()
);
break;
case 'unlock':
state.TokenLock.locked=false;
sendChat('TokenLock','/w gm ' +
getCommandOption_ToggleLock()
);
break;
case '--toggle-lock':
state.TokenLock.locked= !state.TokenLock.locked;
sendChat('TokenLock','/w gm ' +
getCommandOption_ToggleLock()
);
break;
default:
showHelp(who);
break;
}
break;
case '!tl-config':
if(args.includes('--help')) {
showHelp(who);
return;
}
if(!args.length) {
sendChat('','/w "'+who+'" '+
''+
'
'+
'TokenLock v'+version+
'
'+
getConfigOption_AllowMoveOnTurn()+
'
'
);
return;
}
args.forEach((a) => {
let opt=a.split(/\|/);
switch(opt.shift()) {
case '--toggle-allowmoveonturn':
state.TokenLock.config.allowMoveOnTurn = !state.TokenLock.config.allowMoveOnTurn;
sendChat('','/w "'+who+'" '+
''+
getConfigOption_AllowMoveOnTurn()+
'
'
);
break;
default:
sendChat('','/w "'+who+'" ' +
'Unsupported Option:
'+a+''
);
}
});
break;
}
};
const handleMove = (obj,prev) => {
if(state.TokenLock.locked &&
'token' === obj.get('subtype') &&
( !state.TokenLock.config.allowMoveOnTurn ||
(( (((JSON.parse(Campaign().get('turnorder')||"[]"))[0])||{id:false}).id) !== obj.id) ) &&
( obj.get('left') !== prev.left || obj.get('top') !== prev.top || obj.get('rotation') !== prev.rotation )
) {
sendChat('TokenLock','/w gm '
+(obj.get('Name')||(getObj('character',obj.get('represents'))||{get:()=>'Unknown'}).get('name'))
+' just tried to move:'
+(obj.get('left') !== prev.left ? ' X: '+Math.round((obj.get('left')-prev.left)/70) : '' )
+(obj.get('top') !== prev.top ? ' Y: '+Math.round((obj.get('top')-prev.top)/70) : '' )
+(obj.get('rotation') !== prev.rotation ? ' ยบ: '+(obj.get('rotation')-prev.rotation) : '' )
);
if('' !== obj.get('controlledby')) {
obj.set({left: prev.left, top: prev.top, rotation: prev.rotation});
} else if('' !== obj.get('represents') ) {
let character = getObj('character',obj.get('represents'));
if( character && character.get('controlledby') ) {
obj.set({left: prev.left, top: prev.top, rotation: prev.rotation});
}
}
}
};
const checkInstall = () => {
log('-=> TokenLock v'+version+' <=- ['+(new Date(lastUpdate*1000))+']');
if( ! state.hasOwnProperty('TokenLock') || state.TokenLock.version !== schemaVersion) {
log(' > Updating Schema to v'+schemaVersion+' <');
switch(state.TokenLock && state.TokenLock.version) {
/* falls through */
case 0.1:
state.TokenLock.config={
allowMoveOnTurn: false
};
state.TokenLock.version=schemaVersion;
/* falls through */
case 'UpdateSchemaVersion':
state.TokenLock.version = schemaVersion;
break;
default:
state.TokenLock = {
version: schemaVersion,
config: {
allowMoveOnTurn: false
},
locked: false
};
}
}
};
const registerEventHandlers = () => {
on('chat:message', handleInput);
on('change:graphic', handleMove);
};
on("ready", () => {
checkInstall();
registerEventHandlers();
});
return { };
})();