recursed-xls2lua
Version:
Custom level design for Recursed via spreadsheets
410 lines (393 loc) • 11.8 kB
JavaScript
;
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();