Lua as Data
I want to put a bunch of quest and dialogue data in the game I’m working on, “Escape from Valis”, but I don’t want to write it by hand. What if I could write a Lua data structure using a web app?
The problem
I’m designing a game, “Escape from Valis”, and as an old-school RPG, it’ll naturally include quests and dialogue. This would have to live in some sort of data structure…so how would I put this data in the system without writing it all myself?
A potential solution
The solution that comes to mind would be to write to a data structure using some application, which would have some sort of configuration interface, right? But, there’s a catch.
Lua isn’t the best language for full-on application development. Sure, I could build an entire GUI in Lua with some of the libraries out there, but it’s needlessly difficult.
What if, instead, I could write a Lua data structure in a language better suited to app development? What if I used my Javascript skills, along with some sort of Lua translation tool, to create a data structure in a web app and write it to a .lua
file, much like Tiled does?
The plan
This is entirely possible – Lua lends itself well to data expression. The “table” data structure, Lua’s only compound type, acts as a list, a dictionary, or an object (using “metatables”), all in one neat package.
The format is actually quite similar to JSON, and I discovered through a bit of experimentation I don’t even need a Javascript-based Lua VM! I wrote a simple regex or three that replace key characters in the JSON.stringify
output, namely the “:” character and the square array brackets, like this:
JSON.stringify(luaData, false, 2)
.replace(/"(\w+)":/g, "$1 =")
.replace(/\[/g, "{")
.replace(/\]/g, "}");
If you’re not up on your regexes, the first one simply captures the value in double quotes, and unquotes it, replacing the “:” with a “=”. The other two replace the square brackets. Just like that, we have valid Lua!
From there, you can write the contents to a file and trigger a download:
// Code modified from https://stackoverflow.com/questions/13405129/javascript-create-and-save-file
// Licensed under CC-BY-SA 4.0
// where luaState is an object
const fileContents = JSON.stringify(luaState, false, 2)
.replace(/"(\w+)":/g, "$1 =") // replace quoted properties with bare identifiers and an equals sign
.replace(/\[/g, "{") // replace square brackets in these two regexes
.replace(/\]/g, "}");
const file = new Blob([fileContents], { type: "text/plain" }); // create a file object
const filename = "quests.lua";
if (window.navigator.msSaveOrOpenBlob) { // IE10+
window.navigator.msSaveOrOpenBlob(file, filename);
} else { // Others
let a = document.createElement("a"); // creating a virtual "link" to download
const url = URL.createObjectURL(file);
a.href = url;
a.download = filename; // this is what sets up the actual file download
document.body.appendChild(a);
a.click(); // trigger the download
setTimeout(() => {
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
}, 0); // setTimeout(..., 0) will run on the next tick, ensuring the download runs
}
From here, I’ll probably have to cover some edge cases. Now that I look at the code, null
and undefined
would have to become nil
to be valid Lua (but that’s not a problem in this case). Next, I have to create a dead simple UI using React and Next.js, my weapons of choice. This UI should let me add primitive values to compound objects, nested if I choose.
I need to design a data structure first, though. My next post will most likely cover this in detail.