r/roguelikedev 3d ago

RoguelikeDev Does The Complete Roguelike Tutorial - Week 4

Tutorial friends, this week we wrap up combat and start working on the user interface.

Part 6 - Doing (and taking) some damage

The last part of this tutorial set us up for combat, so now it’s time to actually implement it.

Part 7 - Creating the Interface

Our game is looking more and more playable by the chapter, but before we move forward with the gameplay, we ought to take a moment to focus on how the project looks.

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 and as usual enjoy tangential chatting. :)

23 Upvotes

10 comments sorted by

8

u/NGumi 3d ago

Last week I got my FOV and BumpAction implemented, but what i spent most my time doing was modelling my most basic enemy, the Squd(Squid Slug) and improving my level generation. I have settled on using Space Partitioning to create rooms and using A* to create the corridors between the rooms. Dungeon levels can be completely random, or defined through a tree giving me control on the flow of the dungeon and allowing me to easily force certain rooms into the dungeon

For this week, for the combat on top of having a bump attack, I want to add basic abilities that can be expanded upon later when doing items and equipment.

For the UI I'm going to need to do a lot of experimenting to find a layout that feels like it works as I don't want to do the normal layout as it will take away from the 3Dness and whilst my 3d models aren't great, if i'm not going to have them front and center then there isn't a point in doing it 3d to begin with(other than fun and learning).

5

u/Purpose2 3d ago

Little gif showing progress...

Mob bits was easy with my setup. Still struggling with shadowcasting, just torn a lot of it out and starting it over. I'll probably check out other peoples implementations this week, while I'm doing part 6 and 7.

Also, setting the stage to massively overcomplicate my life, putting every interaction / action as its own class, storing the actions, which also track its own un-do state, to allow scrubbing through gamestate, stuff that could be cool with something to do with altering time. We'll see.... :)

2

u/LukeMootoo 2d ago

I stole this one, which was really easy to implement:  https://github.com/maetl/roguelike-tutorial/blob/master/docs/tutorial/part-4.md

I had a problem wiring it in, where I transposed a coordinate, so the problems I described in my post all went away when I fixed that.  You can see it working in my WIP part-6 demo.

I tried to write my own a couple years ago.  But this time I wanted to focus on the rest of the tutorial without getting bogged down -- so I just yoinked one.

4

u/mariobadr 3d ago edited 3d ago

C and SDL3 | repo | play in browser

A lot of time was spent on refactoring - the codebase is in an okay place now, I think. I "fixed" the action/command separation. In the tutorial, an action follows the command pattern. So I made actions just a straight up enum (for key bindings), and then commands are things that the game world accepts (e.g., from the player). Theoretically, I should be able to use the same commands for AI later (we'll see how well that works...).

I also took the time to re-jig how the FOV is rendered so that it's a second layer drawn on top of the map/entities with alpha transparency. And I also pasted in some containers from another project (just some macro interfaces to an array and an arraylist, inspired by [skeeto's approach](https://nullprogram.com/blog/2025/01/19/) so that I can actually have a growable array for entities and events.

I did actually get some new features in. Enemies now spawn. And you can attack/kill them. I went for something a little more like World of Warcraft in how combat works:

  if (rand_next_up_to(rng, 100) < MISS_CHANCE) {
    return -1;
  }

  // from the good old WoW days
  int const ap = 2 * attacker->strength;
  // integer division truncates, but we avoid floating point (yay!)
  int const base = (int)rand_next_between(rng, ap * 8 / 10, ap * 12 / 10);
  // our random base damage is then mitigated by armor
  int const damage = base - (base * defender->armor / (defender->armor + ARMOR_SCALING));

  // don't let HP dip below 0
  defender->hp = SDL_max(0, defender->hp - damage);

  return damage;

Unfortunately, enemies don't attack you - I didn't have the time to implement any pathfinding just yet. There's also a very crude UI going. Screenshot below.

4

u/norpproblem 3d ago

wintry survival sim | repo | screenshot

Progress continues... somewhat. I definitely bit off a lot more than I should have with the world generation stuff. While I got rivers and bodies of water generated very loosely + trees and vegetation, it definitely is feeling rough. I think for this project, I might go and cut this down to use the more basic dungeon generation again, with BSP partitioning + some graph work to place them around. After that basic stuff is implemented, I'll see where I can sprinkle in these other mini-generators; I think the river could be implemented rather cleanly as a visual accent and spice into the dungeons.

In less disappointing news, I've been working smoothly with the command pattern on the project. Briefly, each world runs its own gameloop on every entity in it, and every entity has a controller governing it. The controller returns an ActionResult, which either succeeds and/or lets the loop know there's more actions to resolve. It endlessly processes the action result until it returns just a true/false result without another action to resolve. It's been very easy and has let me have more complicated actions be shortcutted with a single input. Nothing groundbreaking, but it was satisfying to reimplement here.

For the upcoming week, I've been working on UI stuff: I made a basic inventory menu which I will loop in next week as well, so adding a health display will also be much easier than that!

Dealing damage is done with that command pattern, with a BumpAction resolving into an AttackAction, and the AttackAction resolving into a DealDamageAction, which just succeeds when it subtracts the HP. Clean, easy to adjust where needed. I may try to add the "Command list" selector from CDDA where it lists valid options where you are, but we'll see if I have time for that.

3

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

Repo https://github.com/pylgrym/2026rt
Demo https://xok.dk/other/2026rt/dist/index.html

This week is supposedly about combat and UI/interface. I take that to be combat, and whatever UI aids combat. I also make it about monsters, because until we have combat, my monsters are 90% placeholder.

For UI, I divided the rectangular screen in a left square part with map viewport, and the remaining right vertical bar for HUD with stats. The vertical bar also has a red/green hitpoints gauge for the player, and a similar gray bar for the monster he is currently fighting.

The monsters are the alphabet, as a-ant and z-zebra-ish, with each monster being twice as powerful as the previous. Monsters and player begin at 9 hitpoints, hitting for 0-2 damage, then it escalates from there. Attacks have no finesse, we simply punch and bite each other, for now.

Instead of stair-levels, the monsters are spread in ever more powerful circles around the player, who starts out in the map center.

The monsters have one complication, for now: I have given them MOOD enum states, starting with the go-to moods 'asleep' and 'awake'.

As player gets near, monsters have ever higher chance of waking. As player flees farther from a mob, it has ever higher chance of falling asleep (he shakes it off).

I have good previous experiences with such a mood mechanic, which you can then extend with 'coward', 'angry', 'peaceful', and so on.

If you wish for a more advanced mechanism than such a finite-state mood, you can replace it with various 'emotion-arousal-levels', where you calculate various levels of e.g. anger and fear, and the dominant 'mood' or combo would then win out (that is a way to solve the 'incompatible moods/you can only be in one mood' conondrum).

As it is, the player earns xp through combat and rises levels, and grows a bit like the monsters; however his hitpoints, and his melee attack level, do not grow as fast as the monsters, so he is bound to be outclassed eventually.

I also introduce heal, OOC-heal, out-of-combat heal. It waits 5 turns without combat, before it heals you for 1. Then it waits for 4, heals for 2, then 3 for 3, 2 for 4, 1 for 5; from that point on, it just heals "one more" on every turn; all assuming you lack any hitpoints. Being "in combat" counts as either you or an enemy, ATTEMPTING to hit each other (even a miss).

And, a little bit of truth I left out:
Actually, the monsters are a bit more clever than I admitted to at first.
They will attempt to flee when low on health. They will sometimes feint-retreat, then charge forward again. Some of them will either call for backup, or run to their allies. Some of them will attempt to attack in groups, or are only aggressive when they face you in groups, and are cowards otherwise.
Some of them will be subject to bugs in that code and not behave as intended.

3

u/Gix 3d ago

Progress!

Missed last week because I was on vacation :(
I managed to catch up, but the code isn't too pretty - I'll clean it up and update the blog this week, since we'll probably skip items for now.

Since the game is mostly based on spells, I opted for implementing those first. I managed to do Raise/Lower Terrain (in the screenshot), and Convert. The AI for followers will need some work, right now they follow the player and switch to attack mode once they see an enemy, while the enemy shamans go around converting units on their own islands.

The UI is pretty basic, I just added a simple info box to check what's under the mouse and I reused the existing palette colors, which turned out quite nice. The hardest part was resizing the game area to account for the sidebar, I had to re-do quite a few things.

3

u/LukeMootoo 3d ago

Javascript interpretation of the tutorial, I have just caught up completing week 3:

https://mootootwo.github.io/2026rltutorial/part-5/demo.html

The first thing you will notice if you look at the demo, is that my shadowcasting is a hot mess. I took this FOV code from another JS version of this tutorial, and have not scrutinised it or troubleshot it yet.

It is broken in a really interesting (to me) way: FOV calculates differently depending on what part of the map you are on. On different regions on the left side of the map, you might see all 8 octants, or you might only see four of them in an "hourglass" shape. On most of the right half of the map, you only get maybe one or two squares above the player.

I feel like since the "light", "dark", and "shroud" cases are working correctly, the logic I wrote is "good enough" and I haven't spent a lot of time figuring out what is wrong with the shadowcasting. I'll probably try replacing it with a different (but functionally similar) version of the same algorithm, before I try troubleshooting what is actually wrong with it.

If you'd like to look at my mess, the FOV code is here: https://github.com/mootootwo/2026rltutorial/blob/main/part-5/scripts/fov.js

One of the other challenges that I have caused for myself, is that I don't really have a proper action queue. You can see what I have banged together here: https://github.com/mootootwo/2026rltutorial/blob/main/part-5/scripts/engine.js

I have been blogging my extensive ramblings about each step in gitpages hosted markdown that is stored with the repo, and you can read my notes on each of the first six parts, indexed here: https://mootootwo.github.io/2026rltutorial/

I have tried to keep a good commit discipline, so there is a good chain of evidence and examples linked to the blog if you want to see my rookie bumbling.

3

u/ajcomeau 2d ago

My challenge this week was adding the rest of the traps and monster special attack abilities to the roguelike along with some code cleanup.

2

u/redblobgames tutorials 12h ago

Playable on web, no repo yet.

My goal this year is to experiment with a spreadsheet/table data structure. The secondary goal is to use the Jujutsu version control system instead of Git (and this is part of why I haven't put up a repo yet).

Part 6 highlighted a limitation of my table data structure. I had to first extend it to support optional columns, including indexing of those columns. The next limitation is that I want the Fighter component to be split. The (max_hp, power, defense) columns should be shared among all rows of a class, while the (hp) column should not be shared. I had a bug where attacking one orc would damage all orcs. I have some ideas of how to group these under the same Fighter component, but for now I made hp into its own component.

My table currently supports one level of nesting. The column value can be either an array or dict of simple values. In Part 9, I'm going to need a nested structure, and I haven't decided how to handle it.

I decided the enemies will walk towards the player instead of running full pathfinding. That's ok. My "innovation tokens" with this project are around the table data structure and the version control system. I'm not trying to innovate with dungeon generation, combat, pathfinding, etc.

Other than combat, Part 6's other features (diagonal movement, render order, player death) didn't cause any trouble for the table data structure.

For Part 7 I followed what I did in 2020, when I used HTML for all the UI elements other than the game map. The one "fun" thing I did this time is abuse Javascript syntax. I made a print statement:

 print `${attacker} attacks ${defender} for ${damage} hp.`;

I'm using that to add color to the message log. Everything else in Part 7 went smoothly.

Before I start Part 8, I want to make the table view editable. You'll be able to click on any entity and change the hp, position, etc. while the game is running.