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

[ChangeTokenImage] I'm having trouble getting this script to do anything

I've installed this script via the Script Library. I've created a Journal Folder named "Token Images" and put two handouts in it with an image each.  Next, I select a token, and type " !ModifyTokenImage --next" into the chat... literally nothing happens.  I try " !ModifyTokenImage --previous"  and nothing happens.  I try " !ModifyTokenImage --gm"  and nothing happens.  Nothing displays in chat, nothing changes on the battlemap, nothing shows up in the API log.  Nothing. What am I doing wrong?
1633272772
The Aaron
Roll20 Production Team
API Scripter
Where are the images on the token from: marketplace or user upload? Are the tokens multisided?
I might suggest using Tokenmod for this?  Are you trying to make a random npc generator?  If so, you don't need to clutter up your journal with journal entries.  Just upload all the images to a folder in the art tab, then drag each one out, plop them down on a blank map, shift z every one of them and copy image address, paste said address into a rollable table entry.  Then use tokenmod's command to change a token image and set the image choice to the rollable table.  Like The Aaron said, you can't use links to the marketplace in roll20.  Otherwise that'd literally ruin their profit margins.   I might recommend instead of putting the image addresses directly into a rollable table you instead paste them into a notepad document in the format required for the Table export API, so that you can easily recover and import the table everywhere.  
1633294024
The Aaron
Roll20 Production Team
API Scripter
I threw together this little script for making Rollable Table Tokens.  Just select a bunch of graphics on the page and run: !make-rtt and it will zip them up in a Rollable Table Token.  it sorts them in order from top to bottom, left to right.  If any graphics are in the marketplace, it will put the dead X on them and not include them.  It places the new token at the lop left of the tokens (or at (0,0 if you have some bizzare selected tokens that are somewhat off screen..) Script: on('ready',()=>{ const s = { err: "padding: 1px 1em; size:.8em; font-weight:bold; background: #cccccc; border:2px solid black; border-radius:1em; color: #990000;" }; const getCleanImgsrc = (imgsrc) => { let parts = imgsrc.match(/(.*\/images\/.*)(thumb|med|original|max)([^?]*)(\?[^?]+)?$/); if(parts) { return parts[1]+'thumb'+parts[3]+(parts[4]?parts[4]:`?${Math.round(Math.random()*9999999)}`); } return; }; const positionalSorter = (a,b) => { let at = Math.round((a.get('top')+17)/35); let bt = Math.round((b.get('top')+17)/35); let al = Math.round((a.get('left')+17)/35); let bl = Math.round((b.get('left')+17)/35); let abt = at-bt; let abl = al-bl; return (0 === abt ? abl : abt); }; const findTraits = (b) => (o) => { let x = parseFloat(o.get('left')); let y = parseFloat(o.get('top')); let w = parseFloat(o.get('width')); let h = parseFloat(o.get('height')); b.minX = Math.min(b.minX,x-(w/2)); b.minY = Math.min(b.minY,y-(h/2)); b.maxX = Math.max(b.minX,x+(w/2)); b.maxY = Math.max(b.minY,y+(h/2)); b.layer = o.get('layer'); b.pageid = o.get('pageid'); return o; }; on('chat:message',msg=>{ if('api'===msg.type && /^!make-rtt(\b\s|$)/i.test(msg.content) && playerIsGM(msg.playerid)){ let who = (getObj('player',msg.playerid)||{get:()=>'API'}).get('_displayname'); let traits = { minX: Number.MAX_SAFE_INTEGER, minY: Number.MAX_SAFE_INTEGER, maxX: -Number.MAX_SAFE_INTEGER, maxY: -Number.MAX_SAFE_INTEGER, layer: 'objects', pageid: '' }; if(0===(msg.selected||[].length)){ sendChat('',`/w "${who}" <div style="${s.err}">Please selected some tokens.</div>`); return; } let images = (msg.selected || []) .map(o=>getObj('graphic',o._id)) .filter(g=>undefined !== g) .sort(positionalSorter) .map(findTraits(traits)) .reduce((m,g)=>{ let i = getCleanImgsrc(g.get('imgsrc')); if(i){ m.push(i); } else { g.set('status_dead',true); } return m; },[]) ; if(images.length){ let token = createObj('graphic',{ pageid: traits.pageid, layer: traits.layer, left: traits.minX||0, top: traits.minY||0, width: 70, height: 70, imgsrc: images[0], sides: images.map(encodeURIComponent).join('|') }); if(token){ toFront(token); } else { sendChat('',`/w "${who}" <div style="${s.err}">Failed to create token!</div>`); } } else { sendChat('',`/w "${who}" <div style="${s.err}">Only marketplace images found!</div>`); } } }); });
Sorry for delay in reply, time zones are a thing.  Anyway... So, I'm not exactly trying to have rollable tokens.  I have a player with a Druid character, and I'd like them to be able to hit a button and change their token image from their normal form to their wild shape forms.  I have tokenmod, but I don't see a way to do this with it... I use it to change the token markers (unconscious, slowed, flying, poisoned, dying, etc). The token images are user upload, not from the marketplace.
1633345881

Edited 1633345902
David M.
Pro
API Scripter
Iunnrais, check out this approach  for using token-mod for wildshape. It does rely on rollable table tokens, so Aaron's handy make-rtt script will be helpful in its construction. You will also need a character sheet for each form, so that the represents property can be changed appropriately and you'll have access to the correct stat block. If you're playing 5e I would make copies of the compendium animals (renamed slightly) so that you can update the mental stats to those of the Druid character without affecting all animals of that type in your game.   I'd also recommend the TokenActionMaker script if you don't already use it. This will make creating token actions for attacks/saves/checks a breeze for all of those animal forms. There's also the alternative of using Angelo's  Wildshape script instead.
Looks like Angelo's script is pretty awesome... but it won't work for me, as I'm a rare D&D 4th Edition DM, and wild shape works completely differently from 5th edition (you don't change character sheets, for instance).  I'll look into the token-mod approach instead... I guess it's time to learn about rollable tables in roll20!  Never really used them before.
1633348374
David M.
Pro
API Scripter
Ah, cool. I'm technically still in a 4e game myself (assuming we ever get back to meeting IRL)! One note if you haven't use rollable table tokens before: it looks like Aaron's script is a shortcut for creating rtt. Normally you have to create the table of images first and then create the token. If you use the script, no table is created, it just jumps right to producing the token. So, in order to add or subtract images, you'll either have to use the script again or use token-mod to edit that instance of the token (check out the token-mod help handout under "Multi-Sided Token Options" for how to do that). 
1633348804

Edited 1633348953
Looks like rtt and token-mod are the perfect fit for what I'm looking for!  I used Aaron's script to create the rtt, and just set the player's character sheet to use it as the default token, so when he drags his sheet to the table it loads automatically-- no need to create it again. Thank you so much for pointing me in the right direction for what I really  wanted to do (make pressing the wild shape power button change the token image), instead of what I said I wanted to do (get the ChangeTokenImage script to work).  Still mildly miffed that I couldn't get that script to work, but I'm much better off now regardless.  Thanks!
1633351692
The Aaron
Roll20 Production Team
API Scripter
Great! =D
1633364978
keithcurtis
Forum Champion
Marketplace Creator
API Scripter
The Aaron said: I threw together this little script for making Rollable Table Tokens.  Just select a bunch of graphics on the page and run: !make-rtt and it will zip them up in a Rollable Table Token.  How did you know this was exactly the tool I was looking for for an upcoming project?
1633365282
The Aaron
Roll20 Production Team
API Scripter
That seems to describe every tool I "just threw together". =D  I still need to productize that deck builder...
Omg The Aaron.  I made 1500 images for my official bestiary session with all of my creatures stored....creatures have between 7 and 15 images to each side of their default token....and I did all of this without your script here linked.  If I had this then, my god my life would have been so much easier lol.  
Can you do the same thing with this but instead of making a rollable token, make them a deck of cards?  Cause if you can, you should offer that to the roll20 guys to replace their mass uploader!
1633371136
The Aaron
Roll20 Production Team
API Scripter
(cough), (cough) The Aaron said: I still need to productize that deck builder... Let me dig that up...
1635775872
David M.
Pro
API Scripter
FYI, in case anyone is interested I modified Aaron's script to optionally create an actual rollable table object with the images as well (to make later minor editing like replacing one image easier). No options for weighting or showplayers (sets to 1 and false), because I basically never use weights for image tables and showplayers is just clicking a checkbox for what would be a minority of cases.  Syntax: !make-rtt                                        //only creates token !make-rtt --Table Name Can Include Spaces        //creates token and rollable table Here's the macro I'm using: !make-rtt --?{Enter Table Name (or leave blank to just create the token)} The modified script: on('ready',()=>{ const s = { err: "padding: 1px 1em; size:.8em; font-weight:bold; background: #cccccc; border:2px solid black; border-radius:1em; color: #990000;" }; const getCleanImgsrc = (imgsrc) => { let parts = imgsrc.match(/(.*\/images\/.*)(thumb|med|original|max)([^?]*)(\?[^?]+)?$/); if(parts) { return parts[1]+'thumb'+parts[3]+(parts[4]?parts[4]:`?${Math.round(Math.random()*9999999)}`); } return; }; const positionalSorter = (a,b) => { let at = Math.round((a.get('top')+17)/35); let bt = Math.round((b.get('top')+17)/35); let al = Math.round((a.get('left')+17)/35); let bl = Math.round((b.get('left')+17)/35); let abt = at-bt; let abl = al-bl; return (0 === abt ? abl : abt); }; const findTraits = (b) => (o) => { let x = parseFloat(o.get('left')); let y = parseFloat(o.get('top')); let w = parseFloat(o.get('width')); let h = parseFloat(o.get('height')); b.minX = Math.min(b.minX,x-(w/2)); b.minY = Math.min(b.minY,y-(h/2)); b.maxX = Math.max(b.minX,x+(w/2)); b.maxY = Math.max(b.minY,y+(h/2)); b.layer = o.get('layer'); b.pageid = o.get('pageid'); return o; }; on('chat:message',msg=>{ if('api'===msg.type && /^!make-rtt(\b\s|$)/i.test(msg.content) && playerIsGM(msg.playerid)){ let who = (getObj('player',msg.playerid)||{get:()=>'API'}).get('_displayname'); let traits = { minX: Number.MAX_SAFE_INTEGER, minY: Number.MAX_SAFE_INTEGER, maxX: -Number.MAX_SAFE_INTEGER, maxY: -Number.MAX_SAFE_INTEGER, layer: 'objects', pageid: '' }; if(0===(msg.selected||[].length)){ sendChat('',`/w "${who}" <div style="${s.err}">Please selected some tokens.</div>`); return; } let tableName; let args = msg.content.split(/\s+--/); if (args.length > 1) { tableName = args[1].trim(); } let images = (msg.selected || []) .map(o=>getObj('graphic',o._id)) .filter(g=>undefined !== g) .sort(positionalSorter) .map(findTraits(traits)) .reduce((m,g)=>{ let i = getCleanImgsrc(g.get('imgsrc')); if(i){ m.push(i); } else { g.set('status_dead',true); } return m; },[]); if(images.length){ let token = createObj('graphic',{ pageid: traits.pageid, layer: traits.layer, left: traits.minX||0, top: traits.minY||0, width: 70, height: 70, imgsrc: images[0], sides: images.map(encodeURIComponent).join('|') }); if(token){ toFront(token); } else { sendChat('',`/w "${who}" <div style="${s.err}">Failed to create token!</div>`); } if (tableName) { let table = createObj('rollabletable',{ name: tableName, showplayers: false }); if (table) { let tableID = table.get('_id'); images.forEach(img => { let tableItem = createObj('tableitem',{ rollabletableid: tableID, avatar: img, name: '', weight: 1 }); }); } else { sendChat('',`/w "${who}" <div style="${s.err}">Failed to create rollable table!</div>`); } } } else { sendChat('',`/w "${who}" <div style="${s.err}">Only marketplace images found!</div>`); } } }); });
1635830364
The Aaron
Roll20 Production Team
API Scripter
Wow!  That's a great addition!  ***YOINK***