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

Rollable Table result as text only?

Is there a way to return the result of a Rollable Table as text only? I'm trying to combine a Rollable table with the Loot Pile script.  But when I do, it adds a 'weird' string. Using the command:  !loot add item [[ 1t[TableName] ]] I get the result:  (From Loot Pile): Added item $[[0]] to loot pile. I'm guessing it's because the result isn't a text string.  But my coding skills aren't strong enough to be certain of that. Ideally I'd love to just write the results of Rollable Table to a Handout, but Loot Pile works well enough. Thanks for reading!
1449862072
The Aaron
Pro
API Scripter
It has to do with the way inline rolls are passed to API scripts. Assuming this is Randy T.'s Loot Pile script, insert this code (the bold part) at line 141 and it should start doing what you want:     var currency = "gp";     var sender;     var args; /* line 141 */ // ######  new code ###### // if(_.has(msg,'inlinerolls')){ msg.content = _.chain(msg.inlinerolls) .reduce(function(m,v,k){                     var ti=_.reduce(v.results.rolls,function(m2,v2){                         if(_.has(v2,'table')){                             m2.push(_.reduce(v2.results,function(m3,v3){                                 m3.push(v3.tableItem.name);                                 return m3;                             },[]).join(', '));                         }                         return m2;                     },[]).join(', '); m['$[['+k+']]']= (ti.length && ti) || v.results.total || 0; return m; },{}) .reduce(function(m,v,k){ return m.replace(k,v); },msg.content) .value(); } // ######  end new code ###### //     state.lootmoney = state.lootmoney || 0;     state.lootpile = state.lootpile || [];
That works! Thank you again, Mr. Scriptomancer.
So I'm sure it's my syntax, but if i wanted to use another Script to send the string "!loot add item [[ 1t[BagorContainterContents] ]]" via the API, how would I do it?  I tried the following code, but it does not work. var text = "!loot add item [[ 1t[BagorContainterContents] ]]"; sendChat ("", text); I get the message:  SyntaxError: Expected "[" or [ |\t] but "1" found.
Inline rolls sent via the API don't play well with leading spaces (Aaron did some investigation here ).  You'll want to use "[[1t[BagorContainerContents] ]]" (I'm pretty sure the trailing space is required, but they may have fixed the problem with three closing square brackets in a row). You'll also run afoul of the fact that API messages aren't processed for content sent via sendChat.  You'll either have to call into the script providing the "!loot" command directly or use something like CommandShell to marshal the command for you (which would require modifying the script providing the "!loot" command to register with CommandShell if it doesn't already do so, then you'd call Shell.sendChat instead of the built-in sendChat).
Thanks for the input, manveti.  You are correct.  The spacing caused the issue.  And thank you for the advance warning about how API messages aren't processed for content via sendChat.  I tried it anyway, and you are correct. So now I'm back to square 1.  I can either try to modify scripts using code I don't really understand, or write my own.  I'd rather have a custom script anyway (so I can have more coins than just gold).  So I guess now I need to learn how to: Return the text-only result of a rollable table in a script. Write data to a Handout w/o overwriting what is already there. Anyone have any simple code snippets that does that?  I'm sure Aaron has code somewhere that does it, but his code goes way over my head.
1449876905
The Aaron
Pro
API Scripter
Are you saying you want to pass the rollable table roll as a parameter like the loot script and expand it like the loot script hack above, or that you want to write a scrip that does the roll on the table internally and finds the resultant text line?
The Aaron said: Are you saying you want to pass the rollable table roll as a parameter like the loot script and expand it like the loot script hack above, or that you want to write a scrip that does the roll on the table internally and finds the resultant text line? Option B, kinda. I want two functions: I would like a function that rolls on a Rollable Table (the exact table to be passed into the function) and returns the result as a text field. I want a separate function that will write to a Handout w/o overwriting the text that is already there. I don't need a script that does both, just the functions themselves.  Ultimately what I want to do roll a random number of times on different Rollable tables and then write each of the results to the same Handout so I can show my players.  I can write the code for the loop portion.  I just struggle when accessing and modifying objects.
1449889660

Edited 1449895581
The Aaron
Pro
API Scripter
Ok, here's a function for #1: // Roll On Table Snippet var rollableTable = (function( ){     "use strict";      var tables = {},         IDtoTableName = {},     nullTableRoller = function(){         return '';     },     buildTableRoller = function(tablename){         var tobj = _.first(findObjs({             type: 'rollabletable',             name: tablename         }));         tables[tablename]=nullTableRoller;         if(tobj){             IDtoTableName[tobj.id]=tablename;             tables[tablename] = (function(){                 var values = _.reduce(                     findObjs({                         type: 'tableitem',                         rollabletableid: tobj.id                     }),                     function(m,e){                         IDtoTableName[e.id]=tablename;                         _.times(e.get('weight'),function(){m.push(e.get('name'));});                         return m;                     },                     []                 );                 return function(){                     return values[randomInteger(values.length)-1];                 };             }());         }     },     handleItemChange = function(obj){         if(IDtoTableName[obj.get('rollabletableid')]){             buildTableRoller(IDtoTableName[obj.get('rollabletableid')]);         }     },     handleTableChange = function(obj){         if(IDtoTableName[obj.id]){             buildTableRoller(IDtoTableName[obj.id]);         }     };     on('change:rollabletable',handleTableChange);     on('change:tableitem',handleItemChange);     on('add:tableitem',handleItemChange);     on('destroy:tableitem',handleItemChange);     return function( tablename ){         if(!tables[tablename]){             buildTableRoller(tablename);         }                  return (function(tab,nam){             return function(n){                 if(n) {                     return _.times(parseInt(n,10)||1,tab[nam]);                 }                 return tab[nam]();             };         }(tables,tablename));     }; }()); It's actually a factory function that builds functions for rolling on tables:   rollableTable( < table name > ) will return a function which rolls on that table.  In the example, test1 is a function that rolls on the table 'test1'.  if you specify a number as a parameter, it will roll that many times and return an array: var test1 = rollableTable ('test1'); log( test1(6) ); // array of 6 entries from test1 If you call it without an argument, it will return a string that is a single result: var test1 = rollableTable ('test1'); log( test1() ); // a single random string from the table. Additionally, this function caches the values of the table so it will be efficient, and will update the cache as new entries are added or removed.  =D Rolling 2 values ever 2 seconds (edit the table and watch as new rows start showing up, old rolls disappear, and the weights change: on('ready', function(){ "use strict"; var test1 = rollableTable('test1'); setInterval(function(){ log( test1(2) ); },2000); }); I'll get you another one for #2 later.   =D
Wow, that function is more complex than I thought it'd be.  But it's also a lot more robust!  It figures that you'd to overdevelop what I needed.  (That's not a criticism, it's a compliment!) I expected a slightly modified version of what you wrote in your first post of the thread.  I had tried modifying that code because after doing some logging, I saw it returned the Rollable Table result as text, but I couldn't figure it out. Unfortunately I'll probably be too busy this weekend to really test it, but if I get a chance I'll put it through its paces and let you know how it works. As for #2, I can't wait...  :) And thanks again!  A while back, when Fantasy Grounds got official 5e D&D support, some of my players wanted to convert to that VTT.  I cited you as one of the reasons I refuse.  I figure over time you'll have developed everything anyone can think of...
1449895199

Edited 1449895534
The Aaron
Pro
API Scripter
Yeah, I was thinking about it and the efficiency seemed important in this case.  It would be pretty costly to keep doing those findObjs() calls if you were calling it multiple times, so I went for caching.  But then I didn't want you to have to restart the API if you added rows, so I needed to invalidate the cache on changes.  And then I needed to make sure if you made a function to do the roll that it would get updated... It just seemed like the natural way to go!  =D As you may have realized while digging in that code, it doesn't actually roll anything.  Inline Rolls are expanded before the message is passed to the API, but the text where the roll was has a placeholder like $[[0]] which is an index into the msg.inlinerolls object which contains the expanded roll.  That code I posted up above just takes the result of each of those rolls and substitutes it back into the message text.  (in fact, I just copied that block of code straight out of handleInput() from TokenMod). The rollableTable() factory function up above finds the table, then finds all it's items (a separate lookup), then builds an array that contains all of the item's names duplicated as many times as it's weight.  That array gets cached and when you call the function, it gets the array from the cache and returns a random entry from it. (or multiple if you provided a parameter). Here's #2.  It's not as showy as the above, mainly because there isn't a good reason to do extra special caching.  Handout updating is a bit peculiar, so I'm glad to get to write this for you.  One thing that has driven me crazy in the past until I figured it out is that you can't get the note's current text and set it in the same function scope.  The setTimeout() with the 0 time lets the write happen after the .get() has exited.  Without that, it would never make an update because something behind the scenes overwrites any changes you make while in the .get().  Also, if you get a note with nothing in it, it will tell you it's contents are the string 'null'.  Both issues are solved with this function. Anyway, here's the function: // Append row(s) to a handout. var appendNote = function(handoutname){ "use strict"; var msgs = _.flatten(_.rest(arguments)), note = _.first(findObjs({ type: 'handout', name: handoutname })); if(note && note.get && msgs) { note.get('notes',function(n){ n = (n && 'null' !== n ? n+'<br>' : ''); setTimeout(function(){ var text=n+msgs.join('<br>'); note.set('notes',text); },0); }); } }; This is just a simple function that takes the name of a handout one or more things to write to it: appendNote('Dinner Options', 'tacos', 'burgers', 'fish'); That will find the note named 'Dinner Options' (case is important) and write 3 lines to it: tacos burgers fish If you have multiple things to write, it's best to do them all in one call as much as possible.  Getting the current value of the note is asynchronous, so if you make calls too often, you might end up losing some rows between.   You can also call with an array of lines instead of as multiple arguments.  This is identical to the above: var food = ['tacos', 'burgers', 'fish']; appendNote('Dinner Options', food ); Just for fun, here's an example that uses this to write the current time into a handout named 'timelog' once every 5 minutes: on('ready',function(){     "use strict";          appendNote('timelog','Time to start!');          setInterval(function(){         appendNote('timelog', 'the current time is: ',Date().toString(),'');     },5000); }); And I'm happy to have you still here.  It's definitely high praise to be told I'm an important reason for member retention. =D  Would that I had more time to write things.  I'm sure I could turn out some truly impressive systems! =D Let me know if you need anything else!
1449895668
The Aaron
Pro
API Scripter
Just a note about the rollableTable() function:  The names of tables are case sensitve. 
I've had time to play w/ these functions.  They are amazing.  I'm running an old-school module and needed a script to help generate the contents of some containers or bags.  So I set up a rollable table with the possible options, but was struggling on how to roll on that table 5d4 times w/o spamming the chat window.  Now I can get the results with one click. Thank you so very much!
I even managed to use your code to give myself a clearHandout function so I can easily empty the Handout I place the container contents into.  So now I have three useful functions!
1450146186
The Aaron
Pro
API Scripter
Very awesome!  Glad you're getting good use of them!  =D