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

Dice Token Creation

In <a href="https://app.roll20.net/forum/post/3281095/slug%7D" rel="nofollow">https://app.roll20.net/forum/post/3281095/slug%7D</a>, &nbsp;the question Are there any APIs that will automatically create dice tokens from a roll? &nbsp;Like the kind you get from dragging-and-dropping from the chat window? &nbsp;I know there are 3D dice, but they don't allow you to move them around after the roll... was asked, and the answer was I'm not aware of any.&nbsp; It could be done with some setup work.&nbsp; The biggest factor to consider is that the API cannot create images that are NOT in a user's library.&nbsp; So images for each face of the die would need to be uploaded.&nbsp; At that point, such dice could be created as you had faces for with a fairly simple API script.&nbsp; If you have even a small amount of Javascript savvy, I could guide you on that project. I would like to know how to do that.
1670880025
The Aaron
Roll20 Production Team
API Scripter
Here's a little script I wrote that builds a Rollable Table Token out of images you select on the tabletop.&nbsp; It contains all the basic concepts needed to do the above. on('ready',()=&gt;{ 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) =&gt; { 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) =&gt; { 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) =&gt; (o) =&gt; { 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=&gt;{ if('api'===msg.type &amp;&amp; /^!make-rtt(\b\s|$)/i.test(msg.content) &amp;&amp; playerIsGM(msg.playerid)){ let who = (getObj('player',msg.playerid)||{get:()=&gt;'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}" &lt;div style="${s.err}"&gt;Please selected some tokens.&lt;/div&gt;`); return; } let tableName = msg.content.split(/\s+--/).slice(1)[0]; let images = (msg.selected || []) .map(o=&gt;getObj('graphic',o._id)) .filter(g=&gt;undefined !== g) .sort(positionalSorter) .map(findTraits(traits)) .reduce((m,g)=&gt;{ 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}" &lt;div style="${s.err}"&gt;Failed to create token!&lt;/div&gt;`); } if (tableName) { let table = createObj('rollabletable',{ name: tableName, showplayers: false }); if (table) { let tableID = table.get('_id'); images.forEach(img =&gt; { let tableItem = createObj('tableitem',{ rollabletableid: tableID, avatar: img, name: '', weight: 1 }); }); } else { sendChat('',`/w "${who}" &lt;div style="${s.err}"&gt;Failed to create rollable table!&lt;/div&gt;`); } } } else { sendChat('',`/w "${who}" &lt;div style="${s.err}"&gt;Only marketplace images found!&lt;/div&gt;`); } } }); });
1670881461
timmaugh
Pro
API Scripter
Just out of curiosity, is your idea that after the roll, you would have a number of small (and flat) tokens on the board each representing one of the dice that was rolled? The look would be of the kind of die rolled, and the face showing would be of the value that resulted? If so, and if you wanted to preserve the 3d dice rolling, then I you will probably want to allow for rolling in one step, and a "look back" feature to the script to get the recent roll for the person issuing the command. In practice this would probably result in a two line macro... something like: [[1d8 + 1d4]] !dicetokens --options --options That would mean you'd have a listener assigned to the script that would watch every message that came through the chat. Where normal listeners might filter for a message intended for this script, this listener would listen to everything and maintain an object where each key was the playerid of the player issuing the message, and the value of that key would be the most recent inlinerolls property of the message object. Then you could always refer back to the last roll if you wanted to regenerate the dice-token set. If you decide on that approach, you are talking about storage of data between messages (rolling in one message; later spawning the dice-tokens in another message). You have a few options for that... ...you can store it in the closure of the script (revealing module pattern -- this would persist for each reboot of the sandbox) ...in the state (this would persist between sessions and between sandbox reboots) ...in a game object (this would persist even between games, able to be transmogged to a new game) The third option, there, is probably overkill. Between the first two options, I am typically loathe to put too much data in the state (it has a size limit), so I'd probably opt for the script-closure level of storage. Of course, if you see a gameplay usage where you would want to potentially return to a die roll that was done some several minutes before, you might want the comfort of knowing it would be preserved even if the sandbox crashed, so the state might be the better option. After that, you might want to agnosticize the script. Do you want it to point to rolltables of particular/exact names that it will create (or people will have to create/populate with their own images in their own game)? If so, it might be easier to let others use the script (your instructions would be clear about what was required), but it would also mean that everyone getting tokens from the table would have the same color. A different thought would be to allow the rollabletable name to be designated in the command line so that multiple versions of a d10 table could be created (for instance), and different people could spawn different colored tokens... or different colors of d10 tokens could represent different things on the VTT (like different types of units).
1670882755

Edited 1670890242
Joab
Pro
The way the game works (PSI*Run), each player rolls 4-6 six sided dice based on their choices. They then assign each die to a position on the game board. So I'm looking to have tokens on the table that represent the values rolled that can be moved around on the table. There is no need for persistence between sessions or even turns.
1670899773
The Aaron
Roll20 Production Team
API Scripter
Ok.&nbsp; For that, I would suggest creating Rollable Table Tokens for each die, then put them on the table and use a script like this to randomize the side when you're ready: on('ready',()=&gt;{ const getCleanImgsrc = (imgsrc) =&gt; { 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 setTokenSide = (token, side) =&gt; { let sides = token.get('sides').split(/\|/).map(decodeURIComponent).map(getCleanImgsrc); if('*'===side){ let cside = token.get('currentSide'); let keys = Object.keys(sides).filter(n=&gt;cside != n); if(keys.length){ side = keys[randomInteger(keys.length)-1]; } else { side = cside; } } else { side = Math.min(sides.length-1, Math.max(0, side)); } if(sides[side]){ token.set({ currentSide: side, imgsrc: sides[side] }); } else { log(`ERROR: "${token.get('name')}" (id: ${token.id}) does not have a user image at index ${side}!`); } }; on('chat:message',msg=&gt;{ if('api'===msg.type &amp;&amp; /^!srs(\b\s|$)/i.test(msg.content)){ let who = (getObj('player',msg.playerid)||{get:()=&gt;'API'}).get('_displayname'); let tokens = (msg.selected || []) .map(o=&gt;getObj('graphic',o._id)) .filter(g=&gt;undefined !== g) .forEach(t=&gt;setTokenSide(t,'*')) ; } }); }); Select the Rollable Table Tokens and run: !srs Make sure the Rollable Table Tokens don't contain Marketplace art (or be sure to download and upload that art to your use library if you want to use it).
That script works, but what I need is the ability to manipulate the results of &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;button type="roll" name="roll_risk" value="/roll @{rollquery}" data-i18n="take-a-risk"&gt;Take a Risk&lt;/button&gt; where &nbsp; &nbsp; &nbsp; &nbsp; rollquery: "(([[@{baseroll}]])+?{@{psiprompt}|@{yesanswer},1|@{noanswer},0}+?{@{harmprompt}|@{yesanswer},1|@{noanswer},0})d6kl[[([[@{baseroll}]])+?{@{psiprompt}}+?{@{harmprompt}}+(@{drophigh})]]dl[[@{droplow}]]sd" such as rolling (( 4 )+1+0)d6kl 5 dl 1 sd ( 6 + 6 + 5 + 3 + 2 ) = 20
1670966569
David M.
Pro
API Scripter
So you need to place a variable number of [6-sided] dice tokens on the board based on query responses and character attributes?&nbsp; A combination of&nbsp; the Scriptcards and SpawnDefaultToken scripts should be able to do this, assuming you had a multi-sided default token set up for a sheet. I have a few things going on this evening, but I'll see if I can get something out.
There are six&nbsp; multi-sided tokens on the table. Currently, the players roll from the character sheet into the chat, and then manually change the tokens to the correct values.
1670986062
David M.
Pro
API Scripter
Ah ok, I was thinking of deleting the existing tokens and respawning a set of new ones.&nbsp;
That would work too.