Building the Quest Editor

Writing Quests is hard.
Implementing them is harder, especially when they're all in a different format of writing.

To solve this, I had an idea.

What if I made a simple drag-and-drop interface for creating the code.

But how do I do that?

Looking back to my earlier days of development, I remembered MIT's app inventor, which uses a drag-and-drop block interface to create code, using Googles Blockly library which is open source and allows for custom blocks with custom code.

I quickly got to work, downloading the library and writing some quick blocks using their guides. After a while, I had the first basic blocks, the quest wrapper, a stage block, some dialogue blocks, as well as a promise block.

Great, so you're done?

Not really.

As with everything, there are setbacks and bugs and various other issues.
To start, I had an issue where the Stage blocks wouldn't attach to the inside of the quest wrapper in the code. After spending hours googling around and looking at the Blockly documentation, I figured out that the Blockly.JavaScript.statementToCode function did what I needed, create a code snippet from the blocks inside of it and returning it as a string I could insert into the code return of my parent block.

Anything else?

You bet there is.

With a new tool that is just the front-end, you need to have some sort of documentation for it so people know how to use the tool, and ideally, you'd like it to look good and be easy to read.

Enter GitBook, a documentation tool that lets you write your documentation in Markdown and have it be rendered in a nice and easy-to-read way. Along with this, it can sync up with a GitHub repository allowing you to write your documentation locally, push it to the master branch and it'll automatically sync, great!

Uh-huh, documentation, cool.

Yeah, it is, but it wasn't a fast job.

For the documentation, I wanted to have a page describing each and every block of the editor, luckily, you can do that with GitBook as it supports a table of contents which lets you specify the path to each page of the documentation.

I created the folders and markdown files for each block, took a screenshot of each block and saved them to the folders of the repository, adding them to each block page and pushed it up!


So you're done with the documentation then?

I wish I was, Billy.

See, you can't just rely on a user understanding how everything fits together when the only documentation is some technical data on each block and its parameters.

So you wrote some examples?

Exactly, Billy! I wrote examples!

Or rather, a single guide for a single quest with three stages spread throughout multiple pages.

I wrote up a small gathering quest, describing each step and how I had done the layout of the writing, making sure that every detail was in the description of the page for how to write a story.

Once I had the story outlined, I started building the quest in the editor, documenting each step of it with screenshots and descriptions on why and how I did each step, starting with the first page being on how to fill in the quest wrapper block.

After that, I wrote a page on how to add stages, how to fill them in and why we needed to add the different stages and described what a stage was and when to use it.

That seems really boring, are you done yet?

Very funny, Billy, we're not done, I still needed to add the first stage documentation to the guide.

I started with the first stage of the story at the top of the page, followed by each step of the process, what blocks to drag out from where, how to configure them as well as adding screenshots of each step to the guide, a very time-consuming and long process, not to mention all the writing. In total, the first stage guide consists of about 107 lines and 5,000 characters along with 13 images.

But it was worth it.

Having created the first stage guide, I could use it for the second and third stages and as they were smaller, it was just a matter of repeating the process with the different blocks.

Are you done now?

Almost, Billy, you should join Jimmy in the patience classes.

Being done with the documentation and a guide, I shifted my focus to making the editor look better and have more features, starting with a save and load feature.

At first, I saved the quests to the browsers local storage, but that wouldn't work cross-computer, so I needed some other way of sharing the quests.

How do you save that stuff?

Good question.

I started out wanting to put it all in the URL of the webpage, but quickly realized that would make extremely long links for no reason, so I opted for saving it as a file instead.

Implementing the file saving was easy using the BlobAPI of browsers and I created a quick function for saving a file with it:

function saveData(data, fileName) {
	var a = document.createElement("a");
	document.body.appendChild(a); = "display: none";

	var json = JSON.stringify(data),
		blob = new Blob([data], {type: "text/plain;charset=utf-8"}),
		url = window.URL.createObjectURL(blob);
	a.href = url; = fileName;;

It creates a hidden link, with a link to the blob containing the data and downloads it as the specified filename by clicking the link.

Loading the file was also easy using the input element set to a type of file, making it a - you guessed it - file input along with the FileReader API.

I created a function for reading the file when the input element changed and got an element and attached it to an event listener on the file input.

function fileChangedHandler(e){
	let file =[0]

	let self = this

	var reader = new FileReader()

	reader.addEventListener("load", function () {

			localStorage.setItem("questEditor", reader.result)
	}, false)

	if (file) {

After this was done, I bound the CTRL+S keyboard combination to my save function and the CTRL+O function to click the hidden file input, thus triggering it to open, allowing users to upload a file to their local end and having it be loaded into the workspace.

Alright, are you done now?

I am, Billy.

As always with development, there are still some features that need to be added and some other bugs to figure out, but for now, you can check it out at and the docs at