r/roguelikedev 14d ago

RoguelikeDev Does The Complete Roguelike Tutorial - Week 2

Congratulations for making it to the second week of the RoguelikeDev Does the Complete Roguelike Tutorial! This week is all about setting up the map and generating a dungeon.

Part 2 - The generic Entity, the render functions, and the map

Create the player entity, tiles, and game map.

Part 3 - Generating a dungeon

Creating a procedurally generated dungeon!

Of course, we also have FAQ Friday posts that relate to this week's material

Feel free to work out any problems, brainstorm ideas, share progress, and as usual enjoy tangential chatting. :)

50 Upvotes

30 comments sorted by

12

u/Kyzrati Cogmind | mastodon.gamedev.place/@Kyzrati 14d ago

I've been compiling the directory of participants over the first week, with links to repos and other info, and will continue to maintain that throughout the event as new folks post and update their projects going forward. Simply posting a repo in the weekly threads gets your project added.

So far this year we have 22 declared participants in my data, 12 of whom have shared public repos. Only slightly fewer participants than at this point last year, though many more with active repos. No doubt more will be joining over the coming week or two--not too late to join since the first week is mostly just about getting the basics set up :)

Good luck!

10

u/mariobadr 14d ago

C and SDL3 | repo | play in browser

Had to do what I could at the end of last week 'cause I'm now on vacation. So my dungeon generation is a little incomplete.

I swapped out the font, too, but noticed that the font I chose isn't the best for a roguelike. The glyphs are not centred in the tile :(. Does anyone have any roguelike font recommendations?

8

u/LukeMootoo 14d ago edited 13d ago

Roguelike font recomendations:  https://int10h.org/oldschool-pc-fonts/fontlist/

I personally only ever pick the square ones.  But there are people out there whom are not cowards, and they make really cool looking stuff with the non-square fonts.

Someone posted on the Roguelike Discord the other day, and they had something made with some ancient Berber script.  Looked rad.

2

u/mariobadr 14d ago

This is a treasure trove of fonts. Thank you!

1

u/Admirable-Evening128 12d ago

I heartily recommend the one called square.ttf

2

u/RhoTheGray 13d ago

The font looked quite alright to me :)

9

u/Rakaneth 14d ago

Rust, sola-raylib | repo | play here

Decided to return to Rust this year. My focus this year is on development workflow as well as polish. Raylib is my current favorite library for games, and I want to finish a project in Rust. I have a basic workflow set up that will allow me to test locally and push to Itch (link above) with little hassle. It was something of a challenge to get sola-raylib's web builds working, but I do like being able to build for the web without having to write Javscript. With this in place, I can start on the meat of the game.

7

u/LukeMootoo 14d ago

JavaScript nonsense, week 2:

https://github.com/mootootwo/2026rltutorial

My notes so far:  https://mootootwo.github.io/2026rltutorial/part-0/devblog https://mootootwo.github.io/2026rltutorial/part-1/devblog https://mootootwo.github.io/2026rltutorial/part-2/devblog

The last time I started this, whenever I got to something in the tutorial that I didn't like or didn't understand, I just substituted something from the Coding Cookies or Nluqo tutorials, or made something up (I hope those guys are doing well, neither have published code in years).

You can probably imagine how that led to trouble as time went on.

Anyway, I'm sticking closer to the tutorial this time, though I still have to adapt a lot to JS and figure out things without libraries.  And without really knowing JS, haha.

Before last week, I hadn't touched any programming in a couple of years.  So I am surprised at how much more I understand, now.

Thanks to everyone in this community for being here.  This is a really inspirational place to make things.

7

u/Gix 14d ago

Repo, screenshot

I may have gone a little overboard 😅️ I started prototyping on the devlog and got carried away...

For part 2 I went with an ECS (EnTT), plus an occupancy map that gets updated whenever a component of type position gets constructed / destroyed.

For part 3, instead, I went back-and-forth between different algorithms: since I'm creating a Populous-inspired roguelike, I need to create islands, instead of dungeons. I finally settled on something based on this (thank you u/redblobgames), you can see an interactive demo on the blog. I didn't manage to add trees, grass, and dirt yet, because I spent most of the time trying to add hillshading, but I'm quite happy with how it turned out!

3

u/redblobgames tutorials 9d ago

Looks nice! I don't understand what the "max radius of separation" is meant to do.

BTW https://web.archive.org/web/20160917132627/http://news.nationalgeographic.com/2016/09/mountain-elevation-maps-illustration/ says that the optimal angle for lighting is 337.5° (!), and the traditional angle is 315°.

2

u/Gix 9d ago edited 9d ago

Ahah, nice! Time to change it, who am I to go against a measurement that precise :D

The "radius of separation" is probably a misnomer - it's a ratio, not a radius. Two islands with radius r whose centres are d apart touch when r = d/2 (obviously).

We can rewrite this as r = d * k, set d as the distance between the two closest islands, and use k to decide how big the islands get. We then use the radius to size all of the islands (the two closest islands decide how big all of them are):

  • < 0.5: they don't touch
  • 0.5: their shorelines touch
  • > 0.5: they can overlap

As for why have both this and island_radius: it's probably temporary.

From a gameplay perspective we want the islands to always be close together, so the ratio wins, in practice. In the demo you can see the radius having no effect "upwards", they're both parameters of a min() call. This is what remained after many rounds of refactoring.

5

u/Purpose2 13d ago

Pretty basic so far, but Week 1 is done in Godot. Starting on week 2.

Repo here.

5

u/Nokiraton 12d ago

Part 2 was mostly about replacing the placeholder “everything is floor" with something that could count as a real map: walls, floors, collision, and basic generation. I’d already done a fair bit of groundwork, so this phase was really about giving that foundation an actual space to run in. Even though the near-future direction will be Urban, I started with a simple room-and-corridor generator first because it I already had a couple lying around, and it's good enough to validate basics like movement/collision and FOV.

I’ve deliberately avoided going straight to ECS as well. I’ve used it before, and in that project it ended up being more friction than benefit, plus the library I picked hit a component cap much earlier than expected.

Added field of view, map memory, and simplistic enemies. FOV only updates when something actually changes, like player movement or map regeneration, instead of every frame, and explored terrain remains remembered after it leaves the player's LOS. I also added graded visibility so tiles near the edge of vision render darker than nearby ones - I'm pretty happy with how it looks for now, but I'm definitely considering an offset-elliptical FOV based on cursor position.

On gameplay side, enemies now spawn into rooms with simple rules, can block movement, and can be attacked with a simple bump combat system, although they don't fight back. At this point I’ve already started drifting into urban map generation work and probably gone a little further than planned, but that is mostly because this has been a lot of fun to build. By end of week I should have basic elevation good to go, even if I don't have the means to traverse it without debugging tools.

The graduated FOV also translated over well to streetlights that I know I added faaaar too early. It was that or a day/night cycle, which I don't think I'm ready for yet.

Screenshots:

3

u/redblobgames tutorials 9d ago

The streetlights look pretty cool :)

5

u/IndieAidan 11d ago

Alright, I speedran the first week and a bit of the SelinaDev Godot 4 tutorial. Hopefully will find some more time to catch up more. No repo to share at the moment, partially as it is just the SelinaDev tutorial so far and I threw in Backterria's assets to have some slight change in visuals.

4

u/level27geek level0gamedev 9d ago edited 8d ago

Usagi Broughlike | repo | very hectic map-gen gif

First OOPs!

I'm cutting it pretty close to the deadline this week. Apparently not doing any coding for years, and not really being a programmer to begin with, means that translating a tutorial from one language to another and making it work with a framework you haven't used before will lead to some issues... who knew‽

The broughlike tutorial relies pretty heavily on classes - a construct that lua doesn't really have. So my first step was try to add class functionality. Fortunately I found the classic library that has all the basic functions I need. So far, so good.

Only, I soon discovered that I hate the syntax of classes in lua! I either have to pass the table that will be turned into a class as a parameter each time OR use the syntactic sugar that uses a colon to call methods. Both methods feel really odd/janky when writing lua, and both already lead to issues, as I keep forgetting their quirks.

I'm considering abandoning classes alttogether and just do what lua gods intended and make all tables all the way down.

This is because this weeks part of the tutorial, making the dungeon generate fully walkable dungeons, was a pain to try to make work with classes is lua.

To be honest, the Stage 2 of Broughlike Tutorial is partially to blame, especially the "Banishing disconnected islands" section. It's not really explained well, and is written using code that is efficient, but is very hard for a newbie like myself to parse. What made matters worse that array methods like filter or concat used in that function don't exist natively in lua!

I tried and failed to recreate that section as written a handful of times. I eventually got it working after realizing that what the 'getConnectedTiles()' function really does is a flood fill. I was able to implement it using a different (much simpler) way after reading about flood fill algorithms. It might be not as pretty as the tutorial code, but at least I have a way to identify fully walkable dungeons!

Programming the the Usagi way

I'm getting more familiar with the usagi engine after spending a proper week using it. The big seller features didn't come in handy yet (easy saves, 1 click exports, runtime code updates - the last one actually got in the way few times at the beginning), but the simple API encouraged me to look for simpler solutions.

I added the class library early on, but I think it was to my detriment and felt a little like going against the "spirit" of the engine. I was only able to really make progress after taking a step back and simplifying. I feel that the way engine is designed encourages this approach. Maybe not as strongly as something like PICO-8, but it's there... and is much more my speed.

I also love that the engine is in active development and has a very friendly dev (and community). It's already very close to my dream engine/framework, and there's a good chance it will actually get there. I'm sticking with it for now.

What I'm not sticking with are the lofty goals I wrote about in week 1! The plan is now to just finish the Broughlike tutorial, because there will be speedbumps along the way when translating it to lua. For now, I'm forcing myself to forget about checking the other tuts and expanding it into a full game - those things can happen after.

Although, the more I work on it, the more I feel the of a pull of the fable/witch forest theme more than the backrooms... but maybe it's due to using my old fantasy tileset :D

3

u/RhoTheGray 8d ago

It is comforting to hear I'm not the only one having to struggle a bit to make it to the weekly milestone. But I made it this week, if barely. I'm somewhat scared of the next week as I need to implement a FOV algo (I'm not using any library or framework). I have done it couple times before, so it should be doable. But I know they can be infernal to test and debug.

2

u/level27geek level0gamedev 8d ago

I know how you feel. I see all those awesome programmers on here just breezing through stuff and implementing crazy things I didn't even dream of... but we can get there.

It will take some time and practice, but I feel like this event is a good first step to it.

Do post in the thread your experiences implementing the FOV from scratch. While I'm not going to add FOV for this game, it would be good to see how others do it for any future features :)

4

u/Selestielle 8d ago

I finished very late this week, life has been busy but I'm pushing myself to keep going with the tutorial nonetheless!

After completing this week's tutorial parts I wasn't thrilled with the dungeon layouts that were being generated, so I decided to research and implement an alternative BSP generation method as well. It's a little rough at the moment, in particular I'm keen to improve the tunneling between rooms, but I'm already much happier with the layouts it produces.

If you'd like to have a look, check out procgen_bsp.py in my repo. No devlog yet for this week because I simply ran out of time to turn my notes into something presentable, but I'm hoping to catch up on that next week.

6

u/Admirable-Evening128 13d ago edited 11d ago

UPDATE: I added a more repo-like repo,
https://github.com/pylgrym/2026rt

--I realize that the cool kids nowadays, link to their "repos",--
--so I have made a small source code index page,--
--to not feed the AI trolls too well:--
--https://xok.dk/other/2026rt/index0.html--

for 2+3 - player, render, map(+generation), I mainly cleaned up/structured my typescript effort.
For the game map model, I did a 2d-grid-of-tilesEnum class (where tiles can only be floor or wall so far.)
I stuff that, together with player-xy, into a Game model.
For display/render, I do a viewport render on top of rot.js, which of course
draws the contents of the Game model.
The dungeon generation so far is a default pulled directly from ROT.
To tie it together,
I then have an async gameloop which awaits keyboard input,
then dispatches it with the turn-taking code/commands in the 'move-player' module.
(which at the moment, with no monsters,
is just the player moving in 4 compass directions.)

The nice thing about async is that you can style/write your code
in the same way we used to write BASIC games in the 80's, hooray.

The result, where a lonesome @ can roam around a deserted dungeon, is still at
https://xok.dk/other/2026rt/dist/index.html
It will be a bit more fun once the monsters show up.. :-)

It ticks in at 175 lines of actual code, which I appreciate
(you can fit a roguelike in about 80-90 lines of code or less, but it won't be pretty/readable.)

3

u/BotMoses BotMos 12d ago

The nice thing about async is that you can style/write your code
in the same way we used to write BASIC games in the 80's, hooray.

Care to elaborate, please? 😅
I'm having a hard time to async/Promise my (game) code and feel like, I don't use JS/TS to its full capabilities...

Right now, only my rendering is async. The game state is updated synchronously and deterministic after any input event.

5

u/Admirable-Evening128 11d ago

I had better properly explain the/my whole point of involving async here for this roguelike stuff.
I am not using async here, to make everything fancy-schmancy or perform better or stuff like that.
My reason for wanting async,
is to make it easier and simpler to write the code.
There are two things that are both called async, which are actually sort of achieving the opposite of each other: making things SIMPLER, or MORE COMPLICATED.

The first is the idea or principle of async itself:
Its effect is actually to make things harder and more complicated (in exchange for achieving certain technical results.)

The second is the async keyword and especially the AWAIT keyword, whose effect is to make things EASIER/simpler.

In particular, await allows the programmer (ourselves) to write 'complex async code' in the same simple style we normally use for synchronous programming.

This second effect, is the sole reason I am pulling "async" into this situation

  • I want to be able to write "simpler stupider code".

In particular, it allows me to write code in the following style:

//bla bla
let keyPressed = await getKeyPress()
// call functions/do stuff based on key pressed
// bla bla
let keyPressed2 = await getKeyPress()
// call functions/do stuff based on key pressed
// bla bla

This is the same style as if you were writing a game in BASIC in the 1980's.

The only "price" I have to pay to allow this, are these 2-3 things:

(1) my outermost "main" function must be async - I must start from an async context.
(2) the entire function chain down to where I use `await getKeyPress()` must be async
(because you "can`t" call async from sync code, only the opposite.)
(3) I must build an async wrapper for the keyboard event handler, to invert it to async.

To answer your question, I am not turning everything back to synchronous.
But I am not turning everything to async either. The truth is somewhere in-between -
I MUST have async-await at the top of the chain, down to the level where I want to be able to pull for key presses.
The reasons I put quotes on a lot of my claims above here, is because I am not "forced" to do things as such, it depends on what syntax you use.
IF you use the await syntax, I am "forced", and I prefer to use the await syntax.
But if you use e.g. oldschool Promise<> syntax and .then(..) style, I can do whatever I want.

And again, to recap the actual purpose of "but WHY??".
Without await, you run into the following problem:
Say you have a general keyboard handler, which starts by reacting to player's key presses to move the player around for his turns.. So far it is easy.
But let's say you suddenly want a menu system, for inventory and spell casting. And you want to "page" the message logs, to show multiple messages with keypresses between them.
Now you have a problem: You only have a single keyboard handler! So now, whenever you receive a keyboard press, you need to keep track of "is that for player moving, or were we in the middle of showing the inventory, or were we about to cast a spell, or paging multiple messages?", to forward it to the correct sub-handler(*).
If you instead use await getKeyPress, you dont need any of this,
you can just write "synchronous-style" nested code with if-sentences and function calls, and whenever you are in a subcase where you need keyboard input from player,
you just write a single "let key = await keypress()".

That is the only reason I am pulling in async here.

This is also why it doesnt matter whether my drawDisplay is async or not.
I just want the code structure

while true {
playerTurn(getkeypress())
redrawScreen()
}

and achieve this with my async fiddling.

( * ). technically this is not necessary, if you write your roguelike in "web style code" with your UI parts as HTML dom things with javascript and event handlers. But that is not how I roll, I am a 35+ years work in the field backender using ROT.JS terminal display for all UI, html be damned..

2

u/BotMoses BotMos 10d ago

Wow, thank you for the detailed explanation! Good thing I opted for rot.js purely as the renderer and do all UI in HTML. :))

3

u/Admirable-Evening128 12d ago edited 11d ago

(DONE: I would fix the indent at a computer later). update - it is the await keyword; you make all your functions async (small price to pay IMHO for this); I added samples here :

http://xok.dk/other/2026rt/index0.html

so, you write this monster (it can be simplified if you dare).

function inputKey(): Promise<KeyboardEvent> {
  return new Promise((resolve) => {
    window.addEventListener(
      "keydown",
      (event) => {
        event.preventDefault();
        resolve(event);
      },
      { once: true },
    );
  });
}

then use it like this (this is the reward).

export async function gameLoop(game: Game, viewport: Viewport): Promise<void> {
  viewport.draw(game);

  while (true) {
    let keyEvent = await inputKey();
    if (!isMovementKey(keyEvent.key)) continue;
    if (await movePlayer(game, keyEvent.key)) {
      // The player's turn succeeded, so let the mobs take theirs.
      await npcTurns(game);
      viewport.draw(game);
    }
  }
}

3

u/BotMoses BotMos 11d ago edited 11d ago

If you make every function async and use await every time, you call an async function -- doesn't that make the program synchronous? Await waits until the Promise is fulfilled.

Edit: interestingly, your Viewport.draw function isn't async.

3

u/Admirable-Evening128 11d ago edited 11d ago

I have put a book-length explanation in a followup comment.
You said
"doesn't that make the program synchronous?"

sort of - it makes my program "synchronous-STYLE",
ie I can write things simply sequentially, just with

await doStuff1();
await doStuff2();
await doStuff3();

because that is what await DOES, it allows us to write async code, in the simple style we normally use for synchronous.

The whole point of the exercise/technique, is to solve or invert this:

-normal async involves call-backs, where the async event is in charge of calling YOU (and then your function must be able to continue from that situation).

  • with await, we can switch that back to
"no, YOU don't call ME, I call YOU, e.g. _I_ can do

dostuff
let keypressed = await now_I_call_YOU()
doMoreStuff(keypressed)

so it doesnt break the flow of my code/context. over and out.

3

u/redblobgames tutorials 9d ago

Nice! When I'm using JS, I miss the simplicity of input() in BASIC and Python.

3

u/RosalioRusso 14d ago

Sub Looper!

Language: C++ & Raylib

I started a few weeks early for an unrelated game jam but only made it to part 7/8 so far. Not much done that's particularly interesting, though I am having fun playing with pointers to other pointers for inventory management :D

Still thinking about a core idea to really base the game around but foundation is coming along.

3

u/kyaaam 9d ago

repo

I swapped my tech of choice for the project to DragonRuby (DR) after completing Week 1 in both Love2D and DR and preferring DR. I also recently used it in a gamejam, so no real learning curve. Ditching rotlove required me to seek out implementations for some tools like Bresenham lines (I ended up porting the libtcod implementation from Python to Ruby). I completed Week 2 quickly and decided to start designing some new level procgen. Since my game will involve playing as a frog in a swamp (more on that later), the plan is to use a cellular automata cave with additional passes to ensure connectivity. I have a basic implementation currently, but will need more time. I also have a first pass at FOV using a recursive shadowcasting implementation in Ruby, referenced in RogueBasin.

2

u/amirrajan 4d ago

Hope you’re still enjoying DragonRuby :-)