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

Tiles changing color when stepped on?

Hi people, I'm trying to end the current campaign with a puzzle but I'm unsure if I can produce the effect I want. In short I would like a floor tile (using a token made into a table with 3 sides) to change color every time a character step on them.  Is this even possible?
1604684552

Edited 1604684859
The Aaron
Roll20 Production Team
API Scripter
Definitely! on('ready',()=>{ 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 findContains = (obj,filter,layer) => { if(obj) { let cx = obj.get('left'), cy = obj.get('top'); filter = filter || (() => true); layer = layer || 'gmlayer'; return findObjs({ _pageid: obj.get('pageid'), _type: "graphic", layer: layer }) .filter(filter) .reduce((m,o) => { let l=o.get('left'); let t=o.get('top'); let w=o.get('width'); let h=o.get('height'); let ol=l-(w/2); let or=l+(w/2); let ot=t-(h/2); let ob=t+(h/2); if( ol <= cx && cx <= or && ot <= cy && cy <= ob ){ m.push(o); } return m; },[]); } return []; }; const onMoveGraphic = (obj,prev) => { if( ['objects','gm'].includes(obj.get('layer')) && ( parseInt(obj.get('left')) !== parseInt(prev.left) || parseInt(obj.get('top')) !== parseInt(prev.top) ) ) { let tiles = findContains(obj,(o)=>o.get('sides').length,'map'); if(tiles.length){ let sides = tiles[0].get('sides').split(/\|/).map(decodeURIComponent); let nextSide = parseInt(tiles[0].get('currentSide'))+1; if(nextSide>=sides.length){ nextSide = 0; } let imgsrc = getCleanImgsrc(sides[nextSide]); if(imgsrc){ tiles[0].set({ currentSide: nextSide, imgsrc: imgsrc }); } else { sendChat('',`/w gm <b>Error, failed to swap to marketplace side: <img style="max-width:3em;max-height:3em;" src="${sides[nextSide]}" />`); } } } }; on('change:graphic',onMoveGraphic); }); This works by finding the graphics on the map layer (with multiple sides) under any token on the objects or gm layer that is moved, then cycling the image to the next one.  Works only on the destination point, so move one tile at a time (or use the keyboard).  It also doesn't check to see if the tile moved to is the same tile it's on already, but I could add that check.  And it only changes the first tile it finds.
Wonderful! Thanks Aaron! :D Please tell me that you had this written already?
1604685990

Edited 1604686133
The Aaron
Roll20 Production Team
API Scripter
HAHAHA, nope, but I had some of the pieces that would have taken up time, so the the only work was in wiring a few things together. =D  I spent most of the time making tiles to test with. =D
Thank you so very much. :)
1604688168
The Aaron
Roll20 Production Team
API Scripter
No problem. =D  Let me know if you need some enhancements.
Would it be possible to have the script disregard tokens named "Door"? Other then that it works flawlessly and I have a large grin on my face in anticipation of tomorrow. :)
1604709590

Edited 1604712425
The Aaron
Roll20 Production Team
API Scripter
Yup, here's the version that ignores doors: on('ready',()=>{ 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 findContains = (obj,filter,layer) => { if(obj) { let cx = obj.get('left'), cy = obj.get('top'); filter = filter || (() => true); layer = layer || 'gmlayer'; return findObjs({ _pageid: obj.get('pageid'), _type: "graphic", layer: layer }) .filter(filter) .reduce((m,o) => { let l=o.get('left'); let t=o.get('top'); let w=o.get('width'); let h=o.get('height'); let ol=l-(w/2); let or=l+(w/2); let ot=t-(h/2); let ob=t+(h/2); if( ol <= cx && cx <= or && ot <= cy && cy <= ob ){ m.push(o); } return m; },[]); } return []; }; const onMoveGraphic = (obj,prev) => { if( ['objects','gm'].includes(obj.get('layer')) && ( parseInt(obj.get('left')) !== parseInt(prev.left) || parseInt(obj.get('top')) !== parseInt(prev.top) ) ) { let tiles = findContains(obj,(o)=>(o.get('sides').length && !/^\s*door\s*$/i.test(o.get('name'))),'map'); if(tiles.length){ let sides = tiles[0].get('sides').split(/\|/).map(decodeURIComponent); let nextSide = parseInt(tiles[0].get('currentSide'))+1; if(nextSide>=sides.length){ nextSide = 0; } let imgsrc = getCleanImgsrc(sides[nextSide]); if(imgsrc){ tiles[0].set({ currentSide: nextSide, imgsrc: imgsrc }); } else { sendChat('',`/w gm <b>Error, failed to swap to marketplace side: <img style="max-width:3em;max-height:3em;" src="${sides[nextSide]}" />`); } } } }; on('change:graphic',onMoveGraphic); });
Whoa. Whoa. Whoa. Grabbing this for future (evil, trap-springing) use! Thanks as usual, Aaron!
1604712443
The Aaron
Roll20 Production Team
API Scripter
(Minor update to the No-Door version to ignore case).
I'm so very happy right now. :D A big thank you once again.
1604716116
The Aaron
Roll20 Production Team
API Scripter
=D Well that makes ME so very happy!  =D
1604784096
vÍnce
Pro
Sheet Author
I want to use this on a map without any ill-effect, just to see the players reactions when the floor changes color as they walk across the room... ;-)
1604792461
Gold
Forum Champion
Dear The Aaron, Please put any/all finished and working API Script Snippits, into the Roll20 One-Click!  It's so much easier and feels safer to get automatic updates. For example I've been using TurnMarker1 for years, and I don't think that is in the One-Click yet? I know it all takes time and energy. Thank you so much for all you do for Roll20 users.