[Today’s random Sourcerer profile: https://sourcerer.io/ekscrypto]

That gold-colored Nintendo cartridge might as well have been made of real gold. It was my precious. It didn’t give me invisibility or special powers, but it unlocked my imagination. I had an entire world in the palm of my hand. Inserting it into the NES deck unlocked that world and, with it, countless hours of enjoyment.

In that small, 128 kilobyte ROM, Nintendo managed to squeeze in the mystical land of Hyrule, a masterpiece that would change adventure role playing games forever. Those 131,072 characters of code and artwork transformed my childhood and nurtured a deep, life-long love of adventure and role-playing.

A Bit of Nostalgia

If you were a kid in the 80’s or 90’s, you probably played a metric ton of video games. If you had a computer and any interest in programming, you undoubtedly took a stab at making your own. Game development during the late twentieth century was many orders of magnitude harder than it is today, but game programmers had an advantage then that is nearly absent now: your audience was generally not graphically demanding.

With few exceptions, modern gamers expect a high amount of graphical polish in a game. In the long run, content and gameplay take precedence over graphics (as Dwarf Fortress readily demonstrates), but if you don’t meet those expectations you must deliver a strong, perhaps overperforming title.

Despite these lofty challenges, retro gaming is on the rise. What used to be a microscopic niche is a vibrant community thanks, in part, to revived interest in older computers and hardware. New games are being released for the Commodore 64, DOS, and other ancient platforms. Gamers are rediscovering the immersion and imaginative gameplay these systems can offer.

On these old systems there are no notifications, no instant messages, no friends list, and no interruptions. The immersion, if you allow it, is complete.

Slinging MUD

Long before computers could handle fancy 3D graphics, MUDs, or Multi-User Dungeons, were the go-to multiplayer role-playing experience. These games allowed users to interact with a virtual world through text commands. They could explore the land, battle monsters, and even fight other humans in this text-based virtual construct.

One of the most popular MUDs, LPMud, was developed by Lars Pensjo. Rather than write the game engine and entire adventure, he focused on developing a MUD virtual machine, one that would allow writers who didn’t know C to author a story and create world content with ease. Through functions named set_description() and add_exit(), he allowed the adventure creator to use shortcuts to define the structure and narrative of the game. The language was so successful it eventually inspired Pike, an interpreted cross-platform dynamic programming system.

LOTS of Installing

To install and play LOTS, you need a functioning copy of Ruby. While 1.9.3 should work, it was developed for 2.x and higher. Many Linux and Mac computers will already have Ruby installed. You can check by running:

ruby -v

on the command line. If you get a command not found, head on over to the Ruby Language page and follow the installation instructions for your platform.

If you already have it installed, you can install LOTS by simply running:

gem install lots

And then run “lots” on the command line to play.

If you wish to examine the source, you may do so at https://github.com/sourcerer-io/lots

It’s a Small World

In Legend of the Sourcerer, I chose to use the Ruby language. My primary motivation is that it is a language that closely resembles how my brain processes programming challenges. I think of everything as an object, and Ruby works well for that. I admit that functional programming has its place, but in developing a game world, object-oriented design has significant advantages.

Rather than use a virtual machine approach like LPMud, I opted for a map-centric approach. Because the map is dynamically generated, each play-through of LOTS is unique. The world map defines the entire player experience. Landmarks, monsters, and even the Oracle of Code himself is placed on this map.

Each time the player takes a step, the map is scanned to see what character is found in memory. If it matches the predefined code of an enemy, for example, the combat flag is toggled and hack-and-slash action begins. If the player reaches a mountain, a description is relayed to the user.

Choose Your Own Adventure

Remember those books that let you be a part of the action by proceeding to page X if you decided to do one thing and page Y if you decided to do another?

Oh, that dates me. I… uh, was… just asking for a friend anyway.

Part of the reason I wrote this game is to demonstrate how a text-based adventure game could be written. There are two methods that you could use to code your adventure: node-based and map-based.

In a node-based game, you would use a structure similar to that of LPMud or those ancient choose your own adventure books. Decisions about how to proceed would be based on nodes with pre-defined decision points. In this structure, you couldn’t choose an arbitrary direction or decision — you must pick from the available options.

In a map-based game, the player is free to roam in all directions. As previously discussed, the map defines the gameplay. This method allows for a more non-linear and “open world” approach, and also lends itself well to object oriented design, which is why I chose it.

Because of this map-based design, you could edit the lib/world.rb file and add more varied landscapes — a snowy field, a barren desert, caves, or even buildings like castles and forts. Since each item takes up one space on the map, simply adding that to the random mix of characters enables this possibility.

The player’s stats are pre-defined in the lib/character.rb file. These can be adjusted to your liking, or additional metrics added to suit your needs.

The enemy journal can be found in lib/enemy.rb. It takes absolutely no programming knowledge to add or edit the enemies in that class file. I defined a few just to get you started, but there is no limit to the array of monsters you could create.

The user interface is defined in the lib/ui.rb class. Most of this code revolves around displaying the “fancy” frames and UTF-8 characters that make up the game display. There’s a generic draw_frame function that can take a string or array and draw a somewhat attractive “wood-like” frame around it.

The story is defined in lib/story.rb. This is where most of the strings displayed to the user live. I created this class with as little code as possible so that writers could add to or edit the story with relative ease.

Adding commands in the main loop is easy as well, as the next section will demonstrate.

Saving the Day

While saving the game is not a feature in LOTS, it could be easily added. By editing the main.rb file and adding a when case to handle the “save” command (or any other command you’d like to name it), then marshalling the player object to JSON, like this:

# Add this to the top of the main.rb file:

require ‘json’ # Then in the main command loop, add:

when "save"

File.write('savegame.lots', JSON.dump player)

You will have saved the player object to a JSON file named “savegame.lots”. You can then restore your character with code like this:

when "load"

player = JSON.load(File.read('savegame.lots'))

The changes above will save your hero, but the actual world isn’t saved. If you want to do that, you’d also need to marshal the world object in a similar manner. You can either save the two objects as one file then split them in the “load” case, or you can save them to two different files.

You can also name the save file after the player’s name rather than use the “savegame.lots” file above to allow for multiple save states and characters.

Tell Net to Adventure

The game does not feature multiplayer capability, but since it runs on any platform and writes no files to the filesystem, it can easily be played by multiple users at the same time. While not true multiplayer, it could be offered on a telnet BBS if the software allowed for the execution of Ruby programs.

Converting LOTS to a multiplayer game is possible. The player and world objects are separate, so turning the game into a server with multithreaded support for each user session wouldn’t require massive code changes. The world object can be shared among all players, and with a few patches you could keep track of a host of users and their interactions with the world.

You could even allow for player-versus-player action by setting the current_enemy variable to antoher player’s object.

Gameplay Tweaking

The gameplay in LOTS is basic and could certainly be enhanced. The player starts with one hundred health and each encounter with an enemy can reduce that without the possibility for healing. I had originally planned to include spellcasting and healing, but time didn’t allow for this. I also considered allowing a slow regeneration of one health per step or two but felt this may make the game too easy.

As it stands now, the game’s difficulty is largely based on luck of the draw. If there are lots of enemies in your way, you’ll have a more difficult time. However, based purely on chance, you may have a relatively clear path to the oracle in the uppermost right-hand corner of the map. This can be addressed by improving the world generation code to be more thoughtful with enemy placement, or by pre-defining your own world and eliminating dynamic world generation altogether. Either change would be relatively easy given the structure of the game and the flexibility of the Ruby programming language.

A Gem of a Game

Another feature of Ruby demonstrated in LOTS is the gem concept. A Ruby gem is simply a packet of code that can be easily installed on other systems. It is defined by the lots.gemspec file and can be built by running:

gem build lots

in the LOTS main directory. A file called lots-1.0.1.gem is created, and this file can be installed on any machine without the original LOTS source code.

If you examine the lots.gemspec file, you’ll see the required variables for creating your own gem. Using this framework is easy and allows for effortless deployment.

In the spec file, you’ll notice the executables variable. Here I define the master “lots” script that executes and loads in the necessary Ruby code to run the game. This program gets placed in the path so that running “lots” on the command line from any folder will start the game.

If you wish to push your modified version to RubyGems.org, you’ll need to rename the gemspec file and change the parameters within it so that you project name will have unique values.

Playing LOTS Like It’s 1989

Ruby runs in DOS. Yes, you read that correctly. You’ll have to go back to version 1.8.4 compiled with DJGPP (a development suite we’ve used before in the Sourcerer blog to write DOS games) to run it, but it’s possible. While I coded LOTS with Ruby 2.x in mind, with a few tweaks it would be simple to run the game in MS-DOS.

The colorize gem is used to provide ANSI graphics which MS-DOS provides with the ANSI.SYS driver, but a potential problem will be the liberal use of UTF-8 characters to display the map. If you can overlook DOS’s inability to show UTF-8, you can play the adventure. Just don’t expect to benefit from a readable world map. This could be easily fixed by redefining the characters specified in the world.rb constants. Since these are checked using the constant value, no changes to the actual gameplay code are required.

Let the Adventure Continue

LOTS is a fun little stand-alone game, but I believe its true power is in it’s flexibility to adapt to a newplot, setting, or playstyle. To that end, the kind folks at Sourcerer have created a repository for the sole purpose of forking into your grand adventure:

https://github.com/sourcerer-io/sourcerer-textrpg-framework

Any forks and game creations (barring any objectionable content) will be featured on our blog! We’ll also merge any changes or improvements back into the LOTS game and/or framework so everyone can benefit.

I hope you’ve enjoyed playing, reading, and hacking on LOTS as much as I have. It’s been a fun project, and I hope to continue adding features. Feel free to fork it on GitHub.