While I admit I get sucked into too many sessions of World of Warcraft, I’ve always enjoyed playing the classic roguelike games such as NetHack and Dungeon Crawl Stone Soup. If you’ve never played these games before I highly encourage you to check them out.

Yeah there’s definitely an explosion of ASCII, but the incredibly deep gameplay more than makes up for it.

Back in 2016, I was inspired by the most recent 7DRL, a competition where participants must make a roguelike in 7 days, and put together a small roguelike, The Temple of Anguish, using JavaScript and Electron.

Temple of Anguish by Wazoo Media

Rot.js one of the JavaScript libraries I relied upon, is developed by Ondrej Zara. It encompasses an interesting set of JavaScript modules all focused around not just ASCII text display, but things like room generation and “lighting” calculations.

Just to make sure everything is up and running, this first post will consist of getting the basic canvas up and running!

We will start with a very basic game structure. This will be contained in the part1 tag of the code repository.

source code for #part1

first things first - initialize the project

Let’s get a new project going to set everything up. Enter your favorite project home and create a new directory for this rogue adventure magnifique - Baby Rogue!

  • mkdir baby-rogue
  • cd baby-rogue
  • npm init
  • npm install webpack webpack-dev-server html-webpack-plugin babel-core babel-loader babel-preset-env --save-dev

setup and test webpack configuration

Rather then repeating myself, I’ve created a mini deep-dive on getting setup with Webpack.

Webpack survival guide

install rot.js library

With our basic Webpack configuration out of the way, let’s dive right away into starting up with Rot.js.

Rot.js is a set of JavaScript libraries, designed to help with roguelike development in browser environment. - rot.js tagline

  • npm install rot-js --save

This will grab the latest version of the Rot.js library from NPM. Due to the magic and powerful Webpack, that’s all we have to do. No extra configuration step is needed.

open ./src/index.js

Let’s help test and verify all of our configuration, by creating a reference to rot.js within our application.

1 let ROT = import('rot-js');
2 let pkg = require('../package.json');
3 
4 window.onload = () => {
5 
6     console.log('baby-rogue v' + pkg.version);
7 
8 };

npm start

Running npm start should now open up your favorite browser to http://localhost:3000 and should result in an empty page. However, upon opening the JavaScript console, you should see the greeting along with our current version.

Now let’s add a small function from Rot.js which checks to see if our current environment can support the library or not via .isSupported().

 1 'use strict';
 2 
 3 const ROT = require('rot-js');
 4 const pkg = require('../package.json');
 5 
 6 window.onload = () => {
 7 
 8     console.log('baby-rogue v' + pkg.version);
 9 
10     if (!ROT.isSupported()) {
11         throw new Error("The rot.js library isn't supported by your browser.");
12     } 
13 };

creating a basic ROT.Display

We’re almost done the first installment here. Our last task to accomplish is to get our basic ROT.Display canvas displaying Baby Rogue.

start with a new Class - Engine

Because why not? With a name like Engine you just KNOW it’s going to be re-factored many times over, so let’s not even worry about it and bite the bullet.

 1 'use strict';
 2 
 3 const ROT = require('rot-js');
 4 
 5 class Engine {
 6 
 7     constructor(options) {
 8         options = (options || {});
 9         this.width = options.width || 80;
10         this.height = options.height || 20;

The ROT.Display object tries to work with character-sized cells for dividing up the Canvas for drawing. In other words, the object doesn’t use the standard 640x480 pixel dimensions, but things like 80x20 cells. In our class contructor, if we’re not defining the width and height, use 80x20 by default.

1         this.display = new ROT.Display({
2             width: this.width, 
3             height: this.height
4         });
5 
6         document.getElementById('root').appendChild(this.display.getContainer());

Here’s where the magic of ROT.Display starts. We’re creating our display object, then to properly fit and communicate with our browser’s DOM, we use .appendChild to stick our underlying container element to the #root element.

1     }
2 
3     getDisplay() {
4         return this.display;
5     }
6 
7 };
8 
9 module.exports = Engine;

drawing ASCII to our Display

Our last task to test out our #part1, codebase is to draw text to our .display. It’s a rather simple and straightforward function with ROT.Display.drawText().

The ROT.Display uses the concept of a foreground and background, which (if you were around in those days), is a throwback to the early days of 2D graphics programming when graphics cards used frontbuffer and backbuffer for optimal sprite displays.

This lets us get really creative with putting levels together, or really any kind of display we want to use where we can display one color of character on top of a different color.

So here’s the deal:

  • create foreground and background colors using ROT.Color.toRGB()
  • format a string putting the two colors together %c{foregroundcolor}%b{backgroundcolor}
  • finally, use drawText() to position where you want the text, and what text to display.
1     let engine = new Engine();
2 
3     let foreground = ROT.Color.toRGB([255, 255, 0]);
4     let background = ROT.Color.toRGB([0, 0, 0]);
5     let colors = "%c{" + foreground + "}%b{" + background + "}";
6     engine.getDisplay().drawText(10, 10, colors + "Baby Rogue");

baby rogue text

We’ll tackle more in #part2 of our Adventure! Remember you can grab the source code for #part1

Stay productive - and like, comment and share please!