Someone stole the Princess! Post-Mortem

Posted by (twitter: @@donpastor82)
August 27th, 2015 5:52 am

TL;DR WARNING!!!

 

Let’s code a roguelike. Should be easy, right?

Oh man, I was SO wrong. A roguelike might be one of the most complex game types to code, given the amount of interactions possible between its elements (just take a look at NetHack).

From the start I planned a short dungeon romp (about 10 floors deep) with no inventory screen. The original plan was an icon to appear over the player’s head when stepping over an equipment piece for exchanging gear (with the future difference in stats reflected in the GUI).

Of course, as monster can’t hold much equipment (they are not that intelligent) I wanted to put emphasis on drinking potions. Lots of potions with crazy effects affecting gameplay.

You can see the details in my Super Awesome Design Doc ©

Greatest concept art ever

Greatest concept art ever

The challenges

A roguelike is NOT a realtime game. Monsters move when you press a key, and the game world is divided in tiles (usually square tiles, but not always). Each key press is a “turn” and, as there are many actors involved in a turn, there must be a way of managing everything.

Another obvious challenge is map generation. In roguelikes every game is different. Maps are procedurally generated, as well as item end enemy location.

Other not-so-obvious challenges involve enemy AI (foes should chase you in an intelligent way) and field of view, where only the visited rooms are drawn in the map (strategy players will know this as ‘fog of war’).

What???

What the hell?

How does a turn work?

I designed the turn management (turn-o-matic in my code) as a fixed series of events:

Player movement → Enemy movement → Effects update (poison, burn, etc)

First the game would check what kind of tile the player wants to move towards. Depending on the tile type different actions will be triggered:

  • If it’s a wall, player won’t move.
  • If a potion, player will pick it up and effects will be applied.
  • If an enemy, combat will be triggered.
  • If a closed door, it will be opened.
  • If a ladder, player will climb and map will change.
  • Finally, if it’s floor or an open door, player will move.

Next would be enemy movement. Enemies chase the player when there is a path available. That was accomplished thanks to the EaysStar.js library, that allows asynchronous A* pathfinding in your game.

For EasyStar to work, you must provide an array filled with numbers and tell which tiles are walkable, and the start and destination coordinates. For example:

      var walkablemap = [[0,0,0,0],[0,1,1,0],[0,1,1,0],[0,0,0,0]];
      estar = new EasyStar.js
      estar.setGrid(walkablemap);
      estar.setAcceptableTiles([1]);
      estar.findPath(foe.posX,foe.posY, player.posX, player.posY, function (path) {
          if (path) {
              //if success then move the player
          }
      }

EasyStar returns a “path” array containing all the calculated steps to the target. So for a turn we would take the second element of the array, as the first one is the origin point:

    moveFoe(path[1].x,path[1].y);

As EasyStar wouldn’t let the foe step over a wall, game must only check if the destination tile is an empty tile (in that case the foe move towards that tile) ot the player (then it triggers combat).

As for the effects updating, things like ‘Poison’, ‘Burning’, ‘Weakened’, ‘Silenced’ and such were planned but not implemented, so this part is empty.

What went well

Tileset generation went smoothly. Cosmigo ProMotion, while not very intuitive, is a great tool for making tilesets. I also tried Pyxel Edit and liked it even more, but sadly the outdated free version was full of bugs, like refusing to load half of the tiles when loading a project.

The game flow coding went smoothly. Almost a pseudocode to code direct translation.

The map rendering worked on the first try. While some of the wall tiles are not drawn correctly, it doesn’t affect gameplay. It will be corrected soon (that should count as a bugfix for the entry, right?).

¡Behold this amazing black screen!

¡Behold this amazing black screen!

What went wrong

Map generation. For the map generation I took a look at this code, and while I got it somewhat working, didn’t have time to fully integrate it with my game. This is something I’m looking for in the post-LD version I’m making.
I ended choosing a random map between 20-something premade maps. While not common it is possible to see a repeated map ingame.

Field of view is not implemented at all. Shouldn’t be hard to generate a rogue-style FOV, where the full room is revealed once it’s in the player’s range. I’ll leave that for the post-LD version.

Content and gameplay balancing. Base game took so long to code that I didn’t have time to implement many of the things I planned: different foes with different behaviours appearing at different depths, player abilities, equipment system and, of course, some eye-candy.

Map generation needs work

Ouch! Map generation needs some work.

Thoughts

Just as last LD, this was a really fun experience. While I didn’t enjoy the theme much, I’ve always wanted to code a roguelike. I did not completely succeed, but at least my entry is in a somewhat playable state. I’m fine with that :-)

See you at next Ludum Dare!

You can play my entry HERE. Feedback is welcome.

Tags: ,


Leave a Reply

You must be logged in to post a comment.

[cache: storing page]