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

Recursive tables question.. really a tables question

October 31 (3 years ago)

Is there a way to import a list of numbered things into a table, vs me having to copy/paste all 200 of them 1 line at a time? 

October 31 (3 years ago)
The Aaron
Roll20 Production Team
API Scripter

I've got a script for that: https://app.roll20.net/forum/post/1144568/script-tableexport-a-script-for-exporting-and-importing-rollable-tables-between-accounts

October 31 (3 years ago)

Edited October 31 (3 years ago)
David M.
Pro
API Scripter

EDIT - Ninja'd!

Yes, the TableExport script can do this (available on 1-click). And to help with generating the correct syntax, I made a Google spreadsheet that you can copy or download to edit with your own values. To use, just input the values in the yellow cells. This generates the syntax in the green cells which you can paste into a macro window to have the TableExport script build the table(s).


Whenever I make a new rollable table I make it in notepad++ first in the tableexport format that way I always have a way of immediately inserting that table into any other session.  Its too bad we can't share them here as they come a lil too close to copyright infringement and we're all too nervous to get yelled at.  Think just about all of us that use tablexport have just about every single dmg/XGE table made in them with extra bits added on.  

October 31 (3 years ago)


David M. said:

EDIT - Ninja'd!

Yes, the TableExport script can do this (available on 1-click). And to help with generating the correct syntax, I made a Google spreadsheet that you can copy or download to edit with your own values. To use, just input the values in the yellow cells. This generates the syntax in the green cells which you can paste into a macro window to have the TableExport script build the table(s).



This! thank you! exactly what i needed! ( i already had the export mod installed.) 

October 31 (3 years ago)


The Aaron said:

I've got a script for that: https://app.roll20.net/forum/post/1144568/script-tableexport-a-script-for-exporting-and-importing-rollable-tables-between-accounts


Already had this installed. thank you for spending your time writing scripts for everyone. 

October 31 (3 years ago)
The Aaron
Roll20 Production Team
API Scripter

My pleasure!

December 11 (3 years ago)

Edited December 11 (3 years ago)
Manny L.
Pro
Marketplace Creator

Sadly, this won't let me export or import my MERP tables ... I'm guessing this is because my tables have formatted content, for example:

table: CT-1_CrushCritical


When I run the command "!export-table --CT-1_CrushCritical" I get the following output:


On a similar vein, when I use the google spreadsheet to try to import an entry into a test table, it goes super-strange. Note the lack of &{template:default} on the table image after the import function runs:

!import-table --TestTable --show
!import-table-item --TestTable --&{template:default} {{name=Missile Weapon vs No Armour}} {{Roll=146-150}} {{Description=27E}} {{Regular Target = [*Click Here*](!rt CT-3_PunctureCritical 1d100+70)}} {{Large or Super-Large Target = [*Click Here*](http://journal.roll20.net/handout/-MShIVMDThE-Ri1Bpb2H)}} --1




Any idea how I can resolve these issues?

December 11 (3 years ago)
The Aaron
Roll20 Production Team
API Scripter

Oh, that's rough. ExportTables predates the existence of Roll Templates. I'll have to add some sort of encoding option. 

December 11 (3 years ago)
Manny L.
Pro
Marketplace Creator

I added an extra value to "escapes" and an extra value to "entities" and it seems to work now, I can import and export and I keep all the formatting etc.

Thanks for the hard work on the initial code Aaron, much obliged!


// Github:   https://github.com/shdwjk/Roll20API/blob/master/TableExport/TableExport.js
// By:       The Aaron, Arcane Scriptomancer
// Contact:  https://app.roll20.net/users/104025/the-aaron

var TableExport = TableExport || (function() {
	'use strict';

	var version  = '0.2.4',
        lastUpdate = 1576529132,
        tableCache = {},
        escapes = {
            '['   : '<%%91%%>',
            ']'   : '<%%93%%>',
            '--' : '<%%-%%>',
            ' --' : '[TABLEEXPORT:ESCAPE]',
            '&' : '<%%amp%%>'
        },
    
    esRE = function (s) {
        var escapeForRegexp = /(\\|\/|\[|\]|\(|\)|\{|\}|\?|\+|\*|\||\.|\^|\$)/g;
        return s.replace(escapeForRegexp,"\\$1");
    },

	ch = function (c) {
		var entities = {
			'<' : 'lt',
			'>' : 'gt',
			"'" : '#39',
			'@' : '#64',
            '*' : 'ast',
            '`' : '#96',
			'{' : '#123',
			'|' : '#124',
			'}' : '#125',
			'[' : '#91',
			']' : '#93',
			'"' : 'quot',
			'-' : 'mdash',
			' ' : 'nbsp',
            '&' : 'amp'
		};

		if(_.has(entities,c) ){
			return ('&'+entities[c]+';');
		}
		return '';
	},

	checkInstall = function() {
        log('-=> TableExport v'+version+' <=-  ['+(new Date(lastUpdate*1000))+']');
	},

	showHelp = function() {
		sendChat('',
            '/w gm '
+'<div style="border: 1px solid black; background-color: white; padding: 3px 3px;">'
	+'<div style="font-weight: bold; border-bottom: 1px solid black;font-size: 130%;">'
		+'TableExport v'+version
	+'</div>'
	+'<div style="padding-left:10px;margin-bottom:3px;">'
		+'<p>This script dumps commands to the chat for reconstructing a rollable table on another campaign.  While this can be done on your own campaigns via the transmogrifier, this script allows you to pass those commands to a friend and thus share your own creative works with others.<p>'
		+'<p><b>Caveat:</b> Avatar images that are not in your own library will be ignored by the API on import, but will not prevent creation of the table and table items.</p>'
	+'</div>'
	+'<b>Commands</b>'
	+'<div style="padding-left:10px;">'
		+'<b><span style="font-family: serif;">!export-table --'+ch('<')+'Table Name'+ch('>')+' [ --'+ch('<')+'Table Name'+ch('>')+' ...]</span></b>'
		+'<div style="padding-left: 10px;padding-right:20px">'
			+'<p>For all table names, case is ignored and you may use partial names so long as they are unique.  For example, '+ch('"')+'King Maximillian'+ch('"')+' could be called '+ch('"')+'max'+ch('"')+' as long as '+ch('"')+'max'+ch('"')+' does not appear in any other table names.  Exception:  An exact match will trump a partial match.  In the previous example, if a table named '+ch('"')+'Max'+ch('"')+' existed, it would be the only table matched for <b>--max</b>.</p>'
			+'<ul>'
				+'<li style="border-top: 1px solid #ccc;border-bottom: 1px solid #ccc;">'
					+'<b><span style="font-family: serif;">--'+ch('<')+'Table Name'+ch('>')+'</span></b> '+ch('-')+' This is the name of a table to export.  You can specify as many tables as you like in a single command.'
				+'</li> '
			+'</ul>'
		+'</div>'
	+'</div>'
    +'<div style="padding-left:10px;">'
		+'<b><span style="font-family: serif;">!import-table --'+ch('<')+'Table Name'+ch('>')+' --'+ch('<')+'[ show | hide ]'+ch('>')+'</span></b>'
		+'<div style="padding-left: 10px;padding-right:20px">'
			+'<p>This is the command output by <b>!export-table</b> to create the new table.  You likely will not need issue these commands directly.</p>'
			+'<ul>'
				+'<li style="border-top: 1px solid #ccc;border-bottom: 1px solid #ccc;">'
					+'<b><span style="font-family: serif;">--'+ch('<')+'Table Name'+ch('>')+'</span></b> '+ch('-')+' This is the name of the table to be create.'
				+'</li> '
    			+'<li style="border-top: 1px solid #ccc;border-bottom: 1px solid #ccc;">'
					+'<b><span style="font-family: serif;">--'+ch('<')+'[ show | hide ]'+ch('>')+'</span></b> '+ch('-')+' This whether to show the table to players or hide it.'
				+'</li> '
			+'</ul>'
		+'</div>'
	+'</div>'
    +'<div style="padding-left:10px;">'
    	+'<b><span style="font-family: serif;">!import-table-item --'+ch('<')+'Table Name'+ch('>')+' --'+ch('<')+'Table Item Name'+ch('>')+' --'+ch('<')+'Weight'+ch('>')+' --'+ch('<')+'Avatar URL'+ch('>')+'</span></b>'
		+'<div style="padding-left: 10px;padding-right:20px">'
			+'<p>This is the command output by <b>!export-table</b> to create the new table.  You likely will not need issue these commands directly.</p>'
			+'<ul>'
				+'<li style="border-top: 1px solid #ccc;border-bottom: 1px solid #ccc;">'
					+'<b><span style="font-family: serif;">--'+ch('<')+'Table Name'+ch('>')+'</span></b> '+ch('-')+' This is the name of the table to add items to.  <b>Note:</b> unlike for <b>!export-table</b>, this must be an exact name match to the created table.'
				+'</li> '
    			+'<li style="border-top: 1px solid #ccc;border-bottom: 1px solid #ccc;">'
					+'<b><span style="font-family: serif;">--'+ch('<')+'Table Item Name'+ch('>')+'</span></b> '+ch('-')+' This is the name of the table item to create.  <b>Note:</b> Because the string '+ch('"')+' --'+ch('"')+' may occur in a table item name, you may see '+ch('"')+'[TABLEEXPORT:ESCAPE]'+ch('"')+' show up as a replacement in these commands.  This value is corrected internally to the correct '+ch('"')+' --'+ch('"')+' sequence on import.'
				+'</li> '
    			+'<li style="border-top: 1px solid #ccc;border-bottom: 1px solid #ccc;">'
					+'<b><span style="font-family: serif;">--'+ch('<')+'Weight'+ch('>')+'</span></b> '+ch('-')+' This is the weight for this table item and should be an integer value.'
				+'</li> '
    			+'<li style="border-top: 1px solid #ccc;border-bottom: 1px solid #ccc;">'
					+'<b><span style="font-family: serif;">--'+ch('<')+'Avatar URL'+ch('>')+'</span></b> '+ch('-')+' This is the URL for the avatar image of the table item.'
				+'</li> '
			+'</ul>'
		+'</div>'
	+'</div>'
+'</div>'
		);
	},
    nameEscape = (function(){
        var re=new RegExp('('+_.map(_.keys(escapes),esRE).join('|')+')','g');
        return function(s){
            return s.replace(re, function(c){ return escapes[c] || c; });
        };
    }()),
    nameUnescape = (function(){
        var sepacse = _.invert(escapes),
        re=new RegExp('('+_.map(_.keys(sepacse),esRE).join('|')+')','g');
        return function(s){
            return s.replace(re, function(c){ return sepacse[c] || c; });
        };
    }()),

	handleInput = function(msg) {
		var args, matches, tables, tableIDs=[], errors=[], items, itemMatches, accum='';

		if (msg.type !== "api" || !playerIsGM(msg.playerid)) {
			return;
		}

		args = msg.content.split(/\s+/);
		switch(args[0]) {
			case '!import-table':
				args = msg.content.split(/\s+--/);
				if(args.length === 1) {
					showHelp();
					break;
				}
				if(_.has(tableCache,args[1])) {
					sendChat('','/w gm '
						+'<div style="border: 1px solid black; background-color: white; padding: 3px 3px;">'
							+'<span style="font-weight:bold;color:#990000;">Warning:</span> '
							+'Table ['+args[1]+'] already exists, skipping create.'
						+'</div>'
					);
				} else {
					tableIDs=findObjs({type: 'rollabletable', name: args[1]});
					if(tableIDs.length) {
						sendChat('','/w gm '
        					+'<div style="border: 1px solid black; background-color: white; padding: 3px 3px;">'
    							+'<span style="font-weight:bold;color:#990000;">Warning:</span> '
    							+'Table ['+args[1]+'] already exists, skipping create.'
    						+'</div>'
						);
					} else {
						tableIDs=createObj('rollabletable',{ 
							name: args[1], 
							showplayers: ('show'===args[2])
						});
						tableCache[args[1]]=tableIDs.id;
					}
				}
				break;

			case '!import-table-item':
				args = msg.content.split(/\s+--/);
				if(args.length === 1) {
					showHelp();
					break;
				}
				args[2] = nameUnescape(args[2]);
				if(!_.has(tableCache,args[1])) {
					tableIDs=findObjs({type: 'rollabletable', name: args[1]});
					if(!tableIDs.length) {
						sendChat('','/w gm '
        					+'<div style="border: 1px solid black; background-color: white; padding: 3px 3px;">'
    							+'<span style="font-weight:bold;color:#990000;">Error:</span> '
    							+'Table ['+args[1]+'] doesn not exist.  Cannot create table item.'
    						+'</div>'
    					);
                        break;
                    } else {
                        tableCache[args[1]]=tableIDs[0].id;
                    }
                }
                createObj('tableitem',{
                    name: args[2],
                    rollabletableid: tableCache[args[1]],
                    weight: parseInt(args[3],10)||1,
                    avatar: args[4]||''
                });
                break;
                
			case '!export-table':
                args = msg.content.split(/\s+--/);
                if(args.length === 1) {
                    showHelp();
                    break;
                }
				tables=findObjs({type: 'rollabletable'});
				matches=_.chain(args)
					.rest()
					.map(function(n){
						var l=_.filter(tables,function(t){
							return t.get('name').toLowerCase() === n.toLowerCase();
						});
						return ( 1 === l.length ? l : _.filter(tables,function(t){
							return -1 !== t.get('name').toLowerCase().indexOf(n.toLowerCase());
						}));
					})
					.value();

				_.each(matches,function(o,idx){
					if(1 !== o.length) {
						if(o.length) {
							errors.push('Rollable Table [<b>'+args[idx+1]+'</b>] is ambiguous and matches '+o.length+' names: <b><i> '+_.map(o,function(e){
								return e.get('name');
								}).join(', ')+'</i></b>');
						} else {
							errors.push('Rollable Table [<b>'+args[idx+1]+'</b>] does not match any names.');
						}
					}
				},errors);

				if(errors.length) {
					sendChat('','/w gm '
						+'<div style="border: 1px solid black; background-color: white; padding: 3px 3px;">'
							+'<div><span style="font-weight:bold;color:#990000;">Error:</span> '
							+errors.join('</div><div><span style="font-weight:bold;color:#990000;">Error:</span> ')
							+'</div>'
						+'</div>'
					);
					break;
				}

				if( ! errors.length) {
					matches=_.chain(matches)
						.flatten(true)
						.map(function(t){
							tableIDs.push(t.id);
                            return t;
						})
						.value();

					items=findObjs({type: 'tableitem'});
					itemMatches=_.chain(items)
						.filter(function(i){
							return _.contains(tableIDs,i.get('rollabletableid'));
						})
                        .reduce(function(memo,e){
                            if(!_.has(memo,e.get('rollabletableid'))) {
                                memo[e.get('rollabletableid')]=[e];
                            } else {
                                memo[e.get('rollabletableid')].push(e);
                            }
                            return memo;
                        },{})
						.value();
                    _.each(matches, function(t){
                        accum+='!import-table --'+nameEscape(t.get('name'))+' --'+(t.get('showplayers') ? 'show' : 'hide')+'<br>';
                        _.each(itemMatches[t.id], function(i){
                            accum+='!import-table-item --'+nameEscape(t.get('name'))+' --'+nameEscape(i.get('name'))+' --'+i.get('weight')+' --'+i.get('avatar')+'<br>';
                        });
                    });
                    sendChat('', '/w gm '+accum);

				}
				break;
				
				
		}

	},
    handleRemoveTable = function(obj){
        tableCache = _.without(tableCache,obj.id);
    },

	registerEventHandlers = function() {
		on('chat:message', handleInput);
        on('destroy:rollabletable', handleRemoveTable);
	};

	return {
		CheckInstall: checkInstall,
		RegisterEventHandlers: registerEventHandlers
	};
}());


on("ready",function(){
	'use strict';

	TableExport.CheckInstall();
	TableExport.RegisterEventHandlers();
});


December 12 (3 years ago)
The Aaron
Roll20 Production Team
API Scripter

Ah, great!