// 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 '
'+ 'Tokens are now '+ text+ '. '+ ''+ 'Toggle'+ ''+ '
'; }; const getConfigOption_AllowMoveOnTurn = () => { const text = (state.TokenLock.config.allowMoveOnTurn ? 'On' : 'Off' ); return '
'+ 'Allow Move on Turn is currently '+ text+ ' '+ ''+ 'Toggle'+ ''+ '
'; }; 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 { }; })();