UNPKG

recursed-xls2lua

Version:

Custom level design for Recursed via spreadsheets

410 lines (393 loc) 11.8 kB
#!/usr/bin/env node ; var buildLevel, copyRoom, duplicateRooms, fs, loadXLSX, main, objectHeight, objectMapping, objectNames, path, saveXLSX, tileChar, tileMapping, tileNames, xlsx; fs = require('fs'); path = require('path'); xlsx = require('xlsx'); loadXLSX = function(data) { var j, len, ref, results, rows, sheet, subname, workbook; workbook = xlsx.read(data, { type: 'binary' }); ref = workbook.SheetNames; results = []; for (j = 0, len = ref.length; j < len; j++) { subname = ref[j]; sheet = workbook.Sheets[subname]; if (subname.length === 31) { console.warn("Warning: Sheet '" + subname + "' has length exactly 31, which may be caused by Google Sheets export truncation"); } rows = xlsx.utils.sheet_to_json(sheet, { header: 1, defval: '' }); rows.subname = subname; results.push(rows); } return results; }; saveXLSX = function(sheets, filename) { var j, len, sheet, workbook; workbook = xlsx.utils.book_new(); for (j = 0, len = sheets.length; j < len; j++) { sheet = sheets[j]; xlsx.utils.book_append_sheet(workbook, xlsx.utils.aoa_to_sheet(sheet), sheet.subname); } return xlsx.writeFile(workbook, filename); }; tileNames = ['solid', 'ledge', 'water', 'acid', 'buoy', 'ledgewet']; tileMapping = { s: 'solid', solid: 'solid', "-": 'ledge', ledge: 'ledge', w: 'water', water: 'water', a: 'acid', acid: 'acid', buoy: 'buoy', bu: 'buoy', ledgewet: 'ledgewet', lw: 'ledgewet' }; tileChar = { solid: 's', ledge: 'l', water: 'w', acid: 'a', buoy: 'b', ledgewet: 'L' }; objectNames = ['player', 'box', 'key', 'lock', 'chest', 'yield', 'crystal', 'diamond', 'record', 'fan', 'bird', 'crux', 'generic']; objectMapping = { p: 'player', player: 'player', b: 'box', box: 'box', k: 'key', key: 'key', d: 'lock', door: 'lock', l: 'lock', lock: 'lock', chest: 'chest', c: 'chest', y: 'yield', "yield": 'yield', cr: 'crystal', crystal: 'crystal', goal: 'crystal', di: 'diamond', diamond: 'diamond', r: 'record', record: 'record', f: 'fan', fan: 'fan', bi: 'bird', bird: 'bird', g: 'generic', generic: 'generic', o: 'generic', oobleck: 'generic' }; objectHeight = { player: 2, box: 1, key: 1, lock: 2.5, chest: 1, "yield": 1, crystal: 1, diamond: 1, record: 1, fan: 1, bird: 1, crux: 1, generic: 1 }; copyRoom = function(room) { var j, len, results, row, string; results = []; for (j = 0, len = room.length; j < len; j++) { row = room[j]; results.push((function() { var k, len1, results1; results1 = []; for (k = 0, len1 = row.length; k < len1; k++) { string = row[k]; results1.push(string); } return results1; })()); } return results; }; duplicateRooms = function(rooms) { var cell, command, commands, j, k, l, len, len1, len2, len3, m, match, newname, ref, ref1, results, room, roomMap, row, x, y; roomMap = {}; results = []; for (j = 0, len = rooms.length; j < len; j++) { room = rooms[j]; roomMap[room.subname] = room; if (match = ((ref = room != null ? (ref1 = room[0]) != null ? ref1[0] : void 0 : void 0) != null ? ref : '').match(/^duplicate:([^:]+)/)) { newname = room.subname; commands = room.slice(1); room = copyRoom(roomMap[match[1]]); room.subname = newname; for (k = 0, len1 = commands.length; k < len1; k++) { command = commands[k]; switch (command[0]) { case 'replace': for (y = l = 0, len2 = room.length; l < len2; y = ++l) { row = room[y]; for (x = m = 0, len3 = row.length; m < len3; x = ++m) { cell = row[x]; if (cell === command[1]) { row[x] = command[2]; } } } } } } results.push(room); } return results; }; buildLevel = function(rooms) { var arg, cell, dy, global, height, i, item, items, j, k, l, len, len1, len2, len3, len4, level, m, n, neighbor, o, object, ref, ref1, ref2, ref3, ref4, ref5, room, roomMap, roomObjects, row, spawn, spawns, tile, tiles, width, x, xActual, y, yActual; level = []; level.push("local wip = { " + ((function() { var j, len, results; results = []; for (j = 0, len = tileNames.length; j < len; j++) { tile = tileNames[j]; results.push(tileChar[tile] + " = \"" + tile + "\""); } return results; })()).join(', ') + " }"); roomMap = {}; for (j = 0, len = rooms.length; j < len; j++) { room = rooms[j]; roomMap[room.subname] = room; level.push("function " + room.subname + "()"); while (room.length > 15 && ((function() { var k, len1, ref, results; ref = room[room.length - 1]; results = []; for (k = 0, len1 = ref.length; k < len1; k++) { cell = ref[k]; if (cell.trim().length) { results.push(cell); } } return results; })()).length === 0) { room.pop(); } height = room.length; width = Math.max.apply(Math, (function() { var k, len1, results; results = []; for (k = 0, len1 = room.length; k < len1; k++) { row = room[k]; results.push(row.length); } return results; })()); if (!(width === 20 && height === 15)) { console.warn("Warning: Room " + room.subname + " dimensions " + width + "x" + height + " instead of 20x15"); } tiles = (function() { var k, ref, results; results = []; for (y = k = 0, ref = height; 0 <= ref ? k < ref : k > ref; y = 0 <= ref ? ++k : --k) { results.push((function() { var l, ref1, results1; results1 = []; for (x = l = 0, ref1 = width; 0 <= ref1 ? l < ref1 : l > ref1; x = 0 <= ref1 ? ++l : --l) { results1.push('.'); } return results1; })()); } return results; })(); spawns = []; roomObjects = (function() { var k, len1, results; results = []; for (y = k = 0, len1 = room.length; k < len1; y = ++k) { row = room[y]; results.push((function() { var l, len2, results1; results1 = []; for (x = l = 0, len2 = row.length; l < len2; x = ++l) { cell = row[x]; items = cell.split(/\s*[,;]\s*/); results1.push((function() { var len3, m, ref, results2; results2 = []; for (m = 0, len3 = items.length; m < len3; m++) { item = items[m]; if (item in tileMapping) { tiles[y][x] = tileChar[tileMapping[cell]]; continue; } else { item = item.trim(); global = item[0] === '!'; if (global) { item = item.slice(1).trim(); } ref = item.split(':'), object = ref[0], arg = ref[1]; if (!(object in objectMapping)) { continue; } object = objectMapping[object]; results2.push({ object: object, global: global, arg: arg }); } } return results2; })()); } return results1; })()); } return results; })(); for (y = k = 0, len1 = roomObjects.length; k < len1; y = ++k) { row = roomObjects[y]; for (x = l = 0, len2 = row.length; l < len2; x = ++l) { items = row[x]; for (m = 0, len3 = items.length; m < len3; m++) { ref = items[m], object = ref.object, global = ref.global, arg = ref.arg; if (object in objectMapping) { if (arg != null) { if ((object === 'chest' || object === 'record') && ((ref1 = arg[0]) !== '"' && ref1 !== "'")) { arg = "\"" + (arg.replace('"', '\\"')) + "\""; } arg = ", " + arg; } else { arg = ""; } for (dy = n = 1, ref2 = objectHeight[object]; 1 <= ref2 ? n < ref2 : n > ref2; dy = 1 <= ref2 ? ++n : --n) { ref5 = (ref3 = (ref4 = roomObjects[y + dy]) != null ? ref4[x] : void 0) != null ? ref3 : []; for (i = o = 0, len4 = ref5.length; o < len4; i = ++o) { neighbor = ref5[i]; if (neighbor.object === object) { roomObjects[y + dy][x].splice(i, 1); break; } } } yActual = y + objectHeight[object] - 1; xActual = x + 0.5; if (global) { spawn = 'Global'; if (object === 'player' || object === 'yield' || object === 'crystal' || object === 'diamond' || object === 'record' || object === 'bird') { console.warn("Warning: " + object + " should not be global"); } } else { spawn = 'Spawn'; } spawns.push(" " + spawn + "(\"" + objectMapping[object] + "\", " + xActual + ", " + yActual + arg + ")"); } } } } level.push(" ApplyTiles(wip, 0, 0, [["); level.push(((function() { var len5, p, results; results = []; for (p = 0, len5 = tiles.length; p < len5; p++) { row = tiles[p]; results.push(row.join('')); } return results; })()).join('\n')); level.push("]])"); level.push.apply(level, spawns); level.push("end"); } level.push(''); return level.join('\n'); }; main = function() { var arg, args, dirname, expand, extension, filename, filename2, filenames, i, j, k, len, len1, level, outname, output, outputDirs, results, sheets, skip; filenames = []; outputDirs = null; expand = false; skip = 0; args = process.argv.slice(2); for (i = j = 0, len = args.length; j < len; i = ++j) { arg = args[i]; if (skip > 0) { skip--; continue; } switch (arg) { case '-o': if (outputDirs == null) { outputDirs = []; } outputDirs.push(args[i + 1]); skip = 1; break; case '-e': case '--expand': expand = true; break; default: filenames.push(arg); } } if (expand) { extension = '-expanded.xlsx'; } else { extension = '.lua'; } results = []; for (k = 0, len1 = filenames.length; k < len1; k++) { filename = filenames[k]; console.log('**', filename); filename2 = path.parse(filename); filename2.base = filename2.base.slice(0, -filename2.ext.length) + extension; sheets = loadXLSX(fs.readFileSync(filename, { encoding: 'binary' })); sheets = duplicateRooms(sheets); if (expand) { output = function(filename) { return saveXLSX(sheets, filename); }; } else { level = buildLevel(sheets); output = function(filename) { return fs.writeFileSync(filename, level); }; } if (outputDirs != null) { results.push((function() { var l, len2, results1; results1 = []; for (l = 0, len2 = outputDirs.length; l < len2; l++) { dirname = outputDirs[l]; outname = path.join(dirname, filename2.base); console.log('->', outname); results1.push(output(outname)); } return results1; })()); } else { outname = path.format(filename2); console.log('->', outname); results.push(output(outname)); } } return results; }; main();