Roll20 uses cookies to improve your experience on our site. Cookies enable you to enjoy certain features, social sharing functionality, and tailor message and display ads to your interests on our site and others. They also help us understand how our site is being used. By continuing to use our site, you consent to our use of cookies. Update your cookie preferences .
×
Create a free account

[Script] TokenResize -- Allows resizing of tokens Automatically and/or Manually

1421707635

Edited 1440707392
I needed to have a script that would allow automatic resizing of a rollable table token, for when a Druid used his Wild Shape, but could find nothing that would do it. So, using The Aaron's format as a template for this, I wrote this up. It's probably not very elegant in the coding itself, but this is my first script written from scratch. The command to set parameters, including turning on and off of automatic resizing, is !tr. This script allows a GM to let his players resize tokens, instead of forcing the GM to spend the time doing it. A GM can turn the function for manual resizing off, which then allows only a GM to use that function. The script uses isGM to determine whether someone is a GM for purposes of this. By default, this option is set to Locked. To resize manually, a person would use the following format: !tr <token_id> # When using the targeting function, one can use @{target|token_id} and select the target to resize, or @{selected|token_id} when targeting is turned off. The numerical value is the number of grid squares across/high the new size should be. For example, if resizing to a large size (10ftx10ft), the area covered would be 2x2, so the scale factor would be 2. The full context would then be for a large creature that can be targeted: !tr @{target|token_id} 2 Additionally, this script allows tokens that use rollable tables to automatically resize based upon the value of the weight field. The script identifies the value, and uses it as the scale factor. This is useful for a Druid who has the ability to change shapes into larger creatures than himself/herself, as well as when using rollable tables for a mounted/dismounted setup (horses are large creatures, PCs are generally medium). In both the automatic, and the manual resizing, the token is resized to the appropriate size, then placed where the original size's top-left corner was. The grid size can be changed to fit the grid size for the campaign. We use a 140 pixel grid size because we have some older people with bad eyes. I make my own 3D figure tokens, so to be able to see this clearly for those with bad eyes, we increased the size. Shameless token design plug... I'll be fine tuning some things here and there for this. I just figured I can't be the only person who really needed, or has, a use for this script, so I wanted to share it with the community at large. Below is a screen shot of the Help chat output, able to be called by either !tr --help or else having less than 2 arguments after !tr. If anyone has any problems, don't hesitate to let me know. If there is a way to also streamline this, let me know as well, so I can learn some more in fixing it. // var TokenResize = TokenResize || (function() {     'use strict';       var version  = '0.2.0',         lastUpdate = 1440332940,         schemaVersion = 0.7,         gridSize = 70,          CheckInstall = function() {         log('-=> TokenResize v'+version+' <=-  ['+(new Date(lastUpdate*1000))+']');                  if( ! _.has(state,'TokenResize') || state.TokenResize.version !== schemaVersion) {             log(' > Updating Schema to v'+schemaVersion+' <');             switch(state.TokenResize && state.TokenResize.version) {                 case 0.1:                     state.TokenResize.config.AutoResize = false;                     state.TokenResize.version = schemaVersion;                     break;                 default:                     state.TokenResize = {                         version: schemaVersion,                         config: {                             PlayerResize: false,                             isSelected: true,                             AutoResize: false                         },                     };                     break;             }         }     },     ch = function (c) {         var entities = {             '<' : 'lt',             '>' : 'gt',             "'" : '#39',             '@' : '#64',             '{' : '#123',             '|' : '#124',             '}' : '#125',             '[' : '#91',             ']' : '#93',             '"' : 'quot',             '-' : 'mdash',             ' ' : 'nbsp'         };           if(_.has(entities,c) ){             return ('&'+entities[c]+';');         }         return '';     },     getConfigOption_AutoResize = function() {         var text = (state.TokenResize.config.AutoResize ? 'On' : 'Off' );         return '<div>'             +'<b>Auto Resize:</b> '             +'<a href="!tr --auto-resize">'                 +text             +'</a>'         +'</div>';     },     getConfigOption_PlayerResize = function() {         var text = (state.TokenResize.config.PlayerResize ? 'On' : 'Off' );         return '<div>'             +'<b>Player Resize:</b> '             +'<a href="!tr --player-resize">'                 +text             +'</a>'         +'</div>';     },     getConfigOption_isSelected = function() {         var text = (state.TokenResize.config.isSelected ? 'Off' : 'On' );         return '<div>'             +'<b>Selection Required:</b> '             +'<a href="!tr --is-selected">'                 +text             +'</a>'         +'</div>';     },     getAllConfigOptions = function() {         return getConfigOption_AutoResize() + getConfigOption_PlayerResize() + getConfigOption_isSelected();     },     showHelp = function(who) {                      sendChat('','/w '+who+' '         +'<div style="border: 1px solid black; background-color: white; padding: 3px 3px;">'             +'<div style="font-weight: bold; border-bottom: 1px solid black;font-size: 130%;">'                     +'TokenResize v'+version                 +'<div style="clear: both"></div>'             +'</div>'             +'<div style="padding-left:10px;margin-bottom:10px;margin-top:10px;">'                 +'<p>TokenResize allows players to resize their tokens when unlocked.'             +'</div>'             +'<b>Commands</b>'             +'<div style="padding-left:10px;">'                 +'<div style="padding-left: 10px;padding-right:20px">'                     +'<b><span style="font-family: serif;"> !tr</span></b> '+ch('-')+' Command to activate script. Used in conjunction with the following options:'                     +'<ul>'                         +'<li><b><span style="font-family: serif;">--help</span></b> '+ch('-')+' Shows the Help screen</li>'                         +'<li><b><span style="font-family: serif;">--auto-resize</span></b> '+ch('-')+' Sets whether TokenResize will auto resize based upon a token'+ch("'")+'s weight size on its rollable table.</li>'                         +'<li><b><span style="font-family: serif;">--player-resize</span></b> '+ch('-')+' Sets whether TokenResize allows for players to resize.</li>'                         +'<li><b><span style="font-family: serif;">--is-selected</span></b> '+ch('-')+' Sets whether TokenResize requires selection.</li>'                     +'</ul>'                 +'</div>'             +'</div>'             +getAllConfigOptions()         +'</div>'         );     },          autoChange = function(obj, prev) {         if ('card' !== obj.get("subtype") && obj.get("sides") && '' !== obj.get("represents")) {             var aid = obj.get("imgsrc"),                 tid = findObjs({                     type: 'tableitem',                     avatar: aid                 }, { caseInsensitive: true })[0],                     weight,                     pheight;                 if(tid) {                     weight = tid.get("weight");                     pheight = obj.get("height");                                          if(tid.get("weight") <= 0 || !state.TokenResize.config.AutoResize) {                         //showHelp();                         return;  // Even though we use weight for the tile size, let's make sure there is something there to avoid errors.                     };                     if ((gridSize * weight) !== obj.get('width')) {                         obj.set({                             width: (gridSize * weight),                             height: (gridSize * weight)                         });                         if ((gridSize * weight) > gridSize) {                             obj.set({                                 top: (prev.top + ((gridSize / 2) * (weight - (pheight / gridSize)))),                                 left: (prev.left + ((gridSize / 2) * (weight - (pheight / gridSize))))                             })                         } else {                             obj.set({                                 top: (prev.top - ((gridSize / 2) * ((pheight / gridSize) - weight))),                                 left: (prev.left - ((gridSize / 2) * ((pheight / gridSize) - weight)))                             })                         }                     } else {                         return;                     }                 }         }     },          handleInput = function(msg) {         var args, who, obj, prev;           if (msg.type !== "api") {     return; }                  var selected = msg.selected;         who=getObj('player',msg.playerid).get('_displayname').split(' ')[0]; args = msg.content.split(" ");     switch(args[0]) {             case '!tr':                 switch(args[1]) {     case '--auto-resize':                         state.TokenResize.config.AutoResize=!state.TokenResize.config.AutoResize;                         sendChat('','/w '+who+' '                             +'<div style="border: 1px solid black; background-color: white; padding: 3px 3px;">'                                 +getConfigOption_AutoResize()                             +'</div>'                         );                         break; case '--player-resize':                         state.TokenResize.config.PlayerResize=!state.TokenResize.config.PlayerResize;                         sendChat('','/w '+who+' '                             +'<div style="border: 1px solid black; background-color: white; padding: 3px 3px;">'                                 +getConfigOption_PlayerResize()                             +'</div>'                         );                         break;                     case '--is-selected':                         state.TokenResize.config.isSelected=!state.TokenResize.config.isSelected;                         sendChat('','/w '+who+' '                             +'<div style="border: 1px solid black; background-color: white; padding: 3px 3px;">'                                 +getConfigOption_isSelected()                             +'</div>'                         );                         break;                                              case '--help':         showHelp(who); break; }                 if( args[2] && !state.TokenResize.config.PlayerResize && !playerIsGM(msg.playerid) ) {                     sendChat("", "/desc Check player resize."); return;                 } else if ( args[2] && !state.TokenResize.config.isSelected && !selected ) { // Only allows a token to be resized if selected. This prevents players from resizing tokens that don't belong to them.             sendChat("", "/desc Select token and try again.");         return; // quit if nothing selected         } else {                     if(args[2]){                         var obj = getObj("graphic", args[1]),                             ptop = obj.get('top'),                             pleft = obj.get('left'),                             pheight = obj.get('height'),                             weight = args[2];     if( (gridSize * weight) !== obj.get('width') ) {                    obj.set({             width: (gridSize * weight),             height: (gridSize * weight)                    })                                 if( (gridSize * weight) > gridSize ) {                                     obj.set({                                         top: (ptop + ((gridSize / 2) * (weight - (pheight / gridSize)))),                                         left: (pleft + ((gridSize / 2) * (weight - (pheight / gridSize))))                                     })                                 } else {                                     obj.set({                                         top: (ptop - ((gridSize / 2) * ((pheight / gridSize) - weight))),                                         left: (pleft - ((gridSize / 2) * ((pheight / gridSize) - weight)))                                     })                                 }                    }                     } }             break; }  }, RegisterEventHandlers = function() { on('change:graphic', autoChange);         on('chat:message', handleInput); };   return { RegisterEventHandlers: RegisterEventHandlers, CheckInstall: CheckInstall }; }()); on("ready",function(){ 'use strict';         TokenResize.CheckInstall();          TokenResize.RegisterEventHandlers(); });
1421711673
Gen Kitty
Forum Champion
Looks nice! Thanks for writing and posting ^_^
Thank you. I'm just a hack really. Most of this was from peering over The Aaron's coding for (literally) hours, trying to figure out what does what, and then researching what I needed to do.
1421717658
The Aaron
Pro
API Scripter
DVery cool! Happy to be a seed in your growth. :) I can tell by the on('ready') that you were studying one of my older scripts, probably TurnMarker? I've condensed the way I check requirements in newer scripts. Nothing wrong with that, just more streamlined now. I do have a few thoughts for you! For allowing players to adjust things about their tokens, I usually restrict them to only operating on tokens in the msg.selected. Since players can only select tokens they control, it's a nice way to give them functionality without needing to chreck lots of permissions. You can see an example of that in my torch script. You have several fuctions that operate in pairs: performAutoOn/performAutoOff, performPlayerOn/performPlayerOff, etc. You could simplify those to performAuto(Boolean), performPlayer(Boolean). You have several properties of the state, but it would be better to have a single property that contains an object. That reduces your footprint in the state object. You'll also be able to take advantage of the versionSchema stuff I do. :)
1421719953

Edited 1421719970
I'll take a look at the Boolean aspect. I know I also wanted to turn the relocation part into a single function that was called when the parameters were met. I just forgot to work on that while I was working on setting up the ability to allow target or selected, rather than just selected. In truth, I only even added that part in case a GM wanted to be able to let spells cast (e.g.: Enlarge/Reduce) to function in a macro. Originally, I only wanted the script to be able to change the rollable tables when the graphic changes, to eliminate the need to change sides, then hit a macro button. After awhile, it just kind of grew into this since I got the !tr command portion to work first, then worked into the side change. Thanks for the tips! :)