Indie game storeFree gamesFun gamesHorror games
Game developmentAssetsComics
SalesBundles
Jobs
Tags

[Devlog] Dungeons of Menacing Monsters

A topic by Coolcoder360 created Nov 01, 2021 Views: 150 Replies: 1
Viewing posts 1 to 2
(+2)

I'm working on a roguelike first person dungeon crawler type game, somewhat inspired by a Nethack and Ultima Underworld.  Basically the idea is its a procedurally generated dungeon crawler where you traverse floors of a dungeon to get to some sort of end goal or "McGuffin" type thing, and then once you get there, you win.  I plan for it to have Permadeath, partly for the ease of not having to have the saving system always work between releases, and partly because that's how Nethack, Moria, ADOM, and many other roguelikes work that I've enjoyed playing.

I've already done a bit of work on this, and I've logged a bit about it at my other devlog/weekly project logging forum that I log at, so I'll make this post a bit long to go over these topics:

  • Technology/libraries I'll be using
  • Technology of how the proc gen works
  • What I have implemented so far
  • What I have planned to complete for this challenge
  • What my schedule is for this challenge during november
  • What I have planned for after the challenge is over (because I'm so good at creeping scope that I've already creeped it far enough to call for more releases after the end of november)
  • What my release plan is, monetization strategy, etc.

Technology/libraries I'm  using to make this game

I'm using C++ and Raylib to write the engine myself.  Mostly because I've never used Raylib before, and C++ is pretty much my default/de-facto tool for making personal projects.

This tech stack has actually worked really well so far and I managed to accidentally implement some menus and character creation back in September when I first found out about this challenge, as well as slip and get the proc gen all working in September and some of October.  I will admit that the type of code that raylib seems to promote using can get fairly noodle-y very quickly, so I suspect eventually my project will reach some sort of End of Life type state where the code base will be more effort to add things to than it is worth.

How the procedural generation works

Wang Tiles.  I know the name is a bit silly, basically you manually create 2D tiles, then define edge connectivity on each tile, where you basically track which sides of the tile have a connecting hallway and where.  This is a great method because it allows you to manually create a "small" subset of possible tiles, and then piece them together to make worlds.

I say "small" because I've actually found cases though where I'm missing some permutation, because you need to basically have manually created every single combination of each connection across 4 sides.  Initially I made my implementation to support multiple types of connection on the tiles, that way I can have more variety, but there's a problem, there are a ton of possible combinations for each connectivity type.

If I represent a tile's connectivity as a 4 digit binary number where 0 is not connected, and 1 being connected, we get 0000 through 1111 combinations. Convert that to a decimal number and that should be the number of combinations, that's 15+1 = 16.  16 tiles I would have to make to have all possible connectivities, not counting if I want to make multiple tiles with the same connectivity to add variety, and you can easily see where having more than one connectivity type for tiles can make things difficult, because then you're not just doing 16 for one type, and 16 for another, you have to also do ones with both types of connectivity, and at that point you're in some kind of advanced combinations/permutations problem that I don't even care to figure out how to solve, but is definitely not a simple solution.

So having multiple kinds of connectivity to my tiles is out, All of them are going to connect at one specific location on each side, and simply either connect there or not connect there.  That simplifies my tile creation quite a bit, but how do we actually hook them up and make something that actually works?

Well we simply need a method to choose a tile based on the surrounding tiles' connectivity.  So I implemented that quite early, with all edges of my world map defined to have connectivity 0, or not connected, then I make a vector of each combination of tile as needed in the process, and pick a random tile from that list of tiles that fit, and then put that in and use that tile's connectivity in the next tile in the map.

I know my description there doesn't make much sense so let me make an example:

I need to pick the first tile.  That will not want to connect at the top, or at the left.  So I define a standard way to write connectivity, I'll define it as being in NESW order, that way I can make a descriptive string for it.  So our first tile, with no connection at the top or the left, is no connection at the N side or the W side.  so we can write it as 0??0 connectivity, or as I defined in my code `0|||0|`.

Then once we have that string which defines our connectivity, we can do something pretty cool.  We just define a std::map<std::string, std::vector<int>> which maps that string of connectivity, to a vector which refers to our tiles somehow.  Then if that map doesn't have an entry for our string, we know it hasn't found all the possible matches for it, so then we can populate it as needed with all the tiles that will fit in with the `0|||0|` connectivity, so we can find everything that has no connection on the N and W sides, and has any type of connectivity on the E and S sides.  Then it's a matter of picking a random item from that vector, and you have your tile.

This is a great method of doing things, now we can choose tiles and go from a specific connectivity we want, to a list of possible tiles.

Here's what it looks like from my early proc gen code:


But there are two problems here:

  1. What if we didn't define a tile for the connectivity we want?
  2. How do we ensure that our generated map has good connectivity and isn't a bunch of tiles with 2 rooms that connect to each other, but don't all connect to many of the other tiles?

First problem is an easy fix, just put a tile with full connectivity on all sides into the 0 slot, and if there are no tiles that work, just choose tile 0 and you have full connectivity, that way the player can always get through all the tiles that are supposed to be connected to each other, even if there isn't actually a tile made which will connect them.

Second problem is a bit more difficult, how do you define connectivity for your full map you want to generate?  I thought of a few possible solutions before I chose one.

Firstly, we designed our tile selection so that it works from any pre-defined connectivity, so we just need a way to define connectivity for tiles in the middle of the map we're generating, not just on the edges.  So we already just need to add a step before tile selection which defines the connectivity inside the map.

I considered manually creating connectivity inside the map, so you can have a few pre-defined map connectivities, one would get chosen when you start generating the level, and then you just slot tiles into that.  This does add more manual work, although would allow manually defining interesting connectivity, and potentially allow to avoid having connectivity for tiles that do not exist.  This extra work is too much, I don't have time to manually create stuff for the levels, I have other things to implement, so this solution is out.

The other solution I thought of is, well, when you think of making sure you can get from one point to another, you think of pathfinding right? how about implementing a pathfinding algorithm? Yeah that's too much work and I'm not doing that.

So what did I actually do? Well I just used a random walk algorithm instead. You start at the middle of the map, then just pick random directions to travel and write the connectivity needed into a data structure as you go, just walk a bunch of tiles somehow based on the amount of tiles needed in the map and you get a guaranteed connection, and you can make a list of tiles visited as you go, with guaranteed connectivity in each of them, which is great because you can ensure that the player spawn and exit are always accessible.  Assuming tiles are internally connected of course.

This looks like this:

How about that for an interesting map layout with good connectivity?

What's implemented so far?

So far I have procedural level generation, as seen above. I also have basic 3d movement, and a map that shows where the player is, where the level exit is, and allows zooming and scrolling:

What I have planned to complete for this challenge in November

Well I have a ton of stuff.  I usually keep a Quire tracker for my game engine project, so i took that and ran with it for this project too.

Here's a screenshot of what I have so far planned out for doing just this month:





Yeah that's a lot of stuff.  I'm categorizing everything with tags, to say what kind of thing is it, and then trying to ballpark on the schedule when things needs to happen and how much time each thing will take, without explicitly estimating actual time for each thing.
This tells me that boy am I in for a long november.

Schedule

Yeah I'm not good about keeping to a schedule. I usually work on things when I'm excited for things and that is going to be a problem since I've lost a bit of the excitement for this project already since I started it in November.

Generally I would expect to probably get 2-3 hours on whatever weekdays I feel like, and then maybe 2-5 hours on weekends when I feel like it.  But I also got a nintendo switch recently so this project will have some competition for my attention.

What's for after the challenge? and Release plan

Yeah so here's the issue, if you look at that list of things I have for november, there's not a lot of actual content, there's just basically the basics, and then some content to just have something playable at the end of November.  Basically I've planned out enough to get the MVP (Minimum Viable Product) out the door at the end of November, and then (Because the jam states that selling your game is an idea) the plan is basically to add more stuff after that November release date.

If you think this sounds like Early Access, you'd be right.  Basically the current plan is to do an Early Access release for a cheaper than final price, and then the price will go up at the end of early access.  I haven't planned out the releases after the first End of November release but that can wait until later.

I did some research of other dungeon crawlers with grid based movement and turn based combat, and found that the common price for them is $15, some are higher near initial release but $15 seems like a reasonable ballpark for a fully completed game of this sort.

At the end of November, it won't be a fully completed game in my mind, it will be just Early Access and have updates later, so I'm thinking releasing with an initial price of $5 or $10 is the reasonable option. I'm leaning towards $5, since that seems like the reasonable ask for the content planned, perhaps a bit on the high side.

After that initial release, its a matter of releasing more features until I feel like the asking price is justified, and/or I get bored of this project.  My hope of what to accomplish from this challenge is to maintain excitement/interest/energy for this project, to be able to release it in an early access state, and kick that out the door on time at the end of november, and then potentially maintain interest in the same project for a few months after the Challenge is over to continue maintaining and updating the project.

If I can get the Early Access out on time without spending an obnoxious amount of time on it, I will likely consider the challenge a success, hopefully I'll have another update next week or later with lots of features implemented, or at least some sort of combat/gameplay.

I got a bit distracted on this project, got a 3d printer, my watch broke so I'm investigating making a smartwatch, lots of stuff.

I planned to write a whole blurb about how I'd be able to finish this all without crunching, wrote a lot of it, then restarted my browser before posting and lost it all.  In that I said that even though I have a 3d printer , 3d printing takes time so I'd still have time for this!  Then I spent all of yesterday and the day before doing nothing but work, and 3d printer stuff, so that's obviously not true and I need to dedicate time more to this instead of the printer if I want to actually complete this project.

So I'm going to try to go more of a fast and frequent on the devlogs, here's what I implemented I think the same day or the day after I wrote that last one:

  • Doors, opening/removing doors when interacted with
  • floor traversal
  • Text log in the UI to tell you what happened

And here's what I have left for a bare minimum to be satisfied with shipping something at the end of the month:

  • Basic combat
  • at least one enemy to have combat with
  • At least one equipable item to combat the enemy with
  • At least one class to fight as, with the stat generation more finalized than it is now
  • A win condition
  • An inventory system
  • A leveling system
  • At least one usable item to recover health
  • Windows and Linux builds working with my Jenkins build server

I think with that stripped down set of tasks, it should be possible to ship that as an MVP/very very early access type thing at the end of November.

Here's what I had planned for initial release that is not in that MVP list:

  • Saving/loading games
  • Additional items
  • Additional classes
  • help/info menu

These would be stretch items that would likely not make it into the initial MVP and would happen afterwards.  Yes I know Saving/Loading is in the stretch items list, but for a roguelike with permadeath, I'm not sure it makes sense to expend effort on being able to save your game when the combat hasn't even been put in yet.

Of course I will also need to still do all the marketing items I have planned, so I think I'll need a couple hours each day, or maybe a few hours on each weekend day in order to get stuff done in time, I'm hoping to have the combat and win condition in sooner rather than later, so I can get some solid test time to know that it is actually winnable.

Likely the shipping price at the end of november will be significantly lower than the planned full release price, which I think is perfectly reasonable given how much would be cut from the initial release, later updates would be planned to add more content, I'll figure out the plan/schedule for later updates later on.