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

Converting modules to metric system

1599147251

Edited 1599147369
StéphaneD
Pro
Sheet Author
API Scripter
Hello there I sometimes buy pre-made D&amp;D5e modules on the marketplace. Problem : The scale used in maps and the dynamic lighting settings on tokens are using the imperial system, but I live in a country that uses the metric system, and my D&amp;D books are translated to metric as well. In France, our characters have a speed of 9 meters, not 30 feet. Fireballs have a radius of 6 meters, not 20 feet. I have been browsing the forums and haven't found any script or tool to convert from imperial to metrics. I suppose the API gurus over there are happy with their miles and ounces, but I'm not ;)&nbsp; Solution : I have created an API script that must be run as a GM on each page of a pre-made campaign, looping through all the tokens attached to an NPC sheet, and converting any distance from feet to meters. The gist is available here :&nbsp; <a href="https://gist.github.com/stephaned68/6cda403867a49619c2a9864d7e7b9223" rel="nofollow">https://gist.github.com/stephaned68/6cda403867a49619c2a9864d7e7b9223</a> I think I have covered most of the token and page objects properties that need conversion, and so far the script did not mess the pages I tried it on, but I'd appreciate any comment from the API gurus in case I missed something. Thanks in advance
1599147814
The Aaron
Roll20 Production Team
API Scripter
I haven't tried it out or dug into it deeply, but so far it looks really clean and well written.&nbsp; I can probably give you some suggestions later (like good ways to batch up lots of changes).&nbsp; One thing that I did notice is you're only converting Legacy Dynamic Lighting.&nbsp; You might consider adding in Updated Dynamic Lighting properties just to future proof it.
1599151014

Edited 1599151123
StéphaneD
Pro
Sheet Author
API Scripter
Thanks for the kind words, especially coming from The (Legendary) Aaron ;) I thought the array of token properties that I am passing to the conversion function on line 121 were both covering LDL and UDL. Did I miss something ? Let the suggestions flow, they will be much appreciated. Have a terrific day !
1599254337

Edited 1599254421
Just thinking out loud: The state is inferred from the UoM of the page. How do you intent to handle tokens from PCs and tokens that are added later from the compendium? Would it be possible to also change the default tokens in characters automatically?
1599292489
StéphaneD
Pro
Sheet Author
API Scripter
Hi Martijn Regarding PC tokens, I am going to create them myself for my players thus will set them up with metric measures. For the tokens added from the compendium while preping a session, I could probably add some code to perform the conversion only for the selected tokens (with a command sych as !metric --selected). If I have to add them during a session, I would probably let it go to avoid slowing down the pace of the game and breaking the immersion The intent of the script is that : I purchase the module on the Roll20 marketplace, create a campaign that includes it, insert the script in the API section of the campaign settings, then run the VTT and start preping the sessions. If needed, I add stuff from the compendium (NPCs and monsters tokens) and then can then run the script on the page I have prepped, to process all the tokens that are on the map. Thx
1599319634

Edited 1599319644
I am wondering what would be the overhead of adding an attribute UoM to the tokens/characters and then convert them when they are dropped on the VTT in the add:graphic event .
1599382544
StéphaneD
Pro
Sheet Author
API Scripter
You mean adding a new property to the token and/or character objects ? That's a question for the Roll20 API folks, then ?
1599413323
keithcurtis
Forum Champion
Marketplace Creator
API Scripter
Long term plans for the marketplace include using a default grid size for place assets: "Room 5x8" so that they will place at the intended size. If that standard ever materializes and is opened to the API (Generally the Marketplace isn't), a scale might be inferred from that value.
1599436560
The Aaron
Roll20 Production Team
API Scripter
StéphaneD said: Thanks for the kind words, especially coming from The (Legendary) Aaron ;) I thought the array of token properties that I am passing to the conversion function on line 121 were both covering LDL and UDL. Did I miss something ? Let the suggestions flow, they will be much appreciated. Have a terrific day ! Oh!&nbsp; You're absolutely right, that does cover UDL as well.&nbsp; I was thinking about the number of settings I had to deal with for TokenMod, but you don't need to worry about those to convert just the units.&nbsp; Nice. So, when doing operations on a lot of objects, you can end up hitting the "Possible infinite loop detected." error.&nbsp; That's caused when the script doesn't return for some amount of time (1-2 minutes).&nbsp; With pages that have a bunch of graphics, it can take a long time to parse them all.&nbsp; That might not be an issue for this script, but it could cause the API to become unresponsive if it's running while other things are also running.&nbsp; To avoid that, you can borrow some concepts from cooperative multitasking, namely voluntarily yielding execution so that other things can run.&nbsp; The easiest way to do that is through something I call "burndown queues".&nbsp; I'm sure it has a real name, but basically, you collect the things you want to work on, then you process one at a time and defer execute between them.&nbsp; It looks like this: const convertTokenToMetric = (token) =&gt; { // filter tokens with a linked character const charId = token.get('represents'); if (charId !== '') { // get the linked character object const character = findObjs({ _type: 'character', _id: charId }); if (character.length === 1) { // convert the measures to metric const converted = convert(token, [ 'light_radius', 'light_dimradius', 'night_vision_distance', 'bright_light_distance', 'low_light_distance' ]); token.set(converted); sendChat('API', `/w gm ${character[0].get('name')} ...converted`); } } }; /** * Convert scale property values from feet to meters for all token on a page * @param {string} pageId Page object identifier */ function convertToMetric(pageId) { // get all tokens on the page const tokens = findObjs({ _type: 'graphic', _subtype: 'token', _pageid: pageId }); const burndown = ()=&gt;{ if(tokens.length&gt;0){ convertTokenToMetric(tokens.shift()); setTimeout(burndown,0); } }; burndown(); } (I didn't test this code.)&nbsp; Basically, you have a list of things to operate on (tokens) and a function that will process the next one in the list and queue a call to itself (the burndown function).
1599589870
StéphaneD
Pro
Sheet Author
API Scripter
Aaron Thanks for the asynchron-ification of my code ;) I think that's roughly equivalent to the DoEvents() that we had to insert here and there in the good ol' Visual Basic days.&nbsp; I have updated the Gist with the code reworked as you suggested, and a couple of other changes : - The conversion of the page settings is refactored as a function that is called only when the last token of the page has been processed (otherwise the page would have been updated before all the tokens are converted). Still not water-proof I guess, especially if an error occurs in the middle of the conversion. I'll see if I can make it even better... - There is a new --selected command line option that allows the script to process only the tokens that have been selected on the tabletop. Thanks again !