articles

Yippee-Ki-Yay: Building a Die Hard Roguelike in C

July 2026

Game dev history

I write PHP and JavaScript for a living. Laravel, Vue, the occasional bit of Python when the mood takes me. I've taken a stab at the C programming language a couple of times but never really stuck with it. Last year I built PixelInvaders: Rogue Mode, a small space-invaders clone with some roguelike properties bolted on.

PixelInvaders: Rogue Mode, a space-invaders clone with roguelike properties bolted on

PixelInvaders: Rogue Mode, last year's space-invaders-meets-roguelike experiment.

After that I moved on and built the foundations of a simple roguelike game. A lot of the fundamentals took a fair bit of time and, like a lot of side projects, it got the initial burst of enthusiasm and then quietly stopped. (We all know how that goes)

An early build of the roguelike engine, glyphs rendered on a grid

The first pass at the roguelike engine, before this week's fresh start.

I hadn't touched C in a couple of months, but I really wanted to pick it back up, and, start fresh with a new idea.

Why Roguelikes/Roguelites?

Two reasons. I like them, and I reckon roguelikes are a genuinely good fit for learning a systems language. They're grid-based, turn-based, and traditionally rendered as simple glyphs rather than sprites, which means you can focus on the interesting logic first and worry about the art later. For anyone getting into game dev programming, that's the whole appeal.

You can still draw glyphs through SDL2 rather than a terminal, so there's a renderer under there, but you keep it deliberately simple: a font, a grid, and some colours.

So when I came back to the project this week, I made a decision. I wanted the foundations of a proper game engine, so rather than treat last year's code as one big undifferentiated game, I'd draw a hard line between the reusable engine bits (the plumbing) and the game bits on top. The engine handles rendering, input, movement, the map, the field of view, the turn loop, and the game handles the fun stuff that actually belongs to a game.

The Setting: Die Hard "Inspired"

I wanted to move away from the traditional fantasy dungeon. I've always liked 80s action films, and it struck me that scaling a tower floor by floor is a really good fit for a roguelike, so, "inspired" by a certain film, that became the setting. Shoot the bad guys, climb the tower, rescue the hostages.

It's still very much alpha. There's no boss yet, John McClane hasn't met his Hans Gruber. But the bones are there, and honestly, it's been satisfying for the most part.

I'm currently going with the title "Thirty Floors", figuring anything closer to "Die Hard" might be a bit too close to copyright for comfort. It only has 5 floors right now, so the title is technically a lie at the moment, but shhh.

Thirty Floors as it stands today, climbing the tower one procedurally generated office at a time.

What's Actually Working

Building games is hard, and doing it part time is harder still. But you can surprise yourself when you just hack at it. Here's where things stand:

  • Turn-based shooting and melee, the core combat loop, whether you're up close or picking targets down a corridor, with a to-hit penalty that grows with distance
  • A cover system, half-height furniture that blocks accuracy rather than sight, so positioning matters; the more cover tiles on the line between you and a target, the worse the shot
  • Ammo types, separate pools for 9mm, shotgun shells and rifle rounds, so the gun you pick up is only as useful as the ammo you're carrying for it
  • Basic inventory, nothing fancy yet, just a small fixed-size pack that holds up to five items. See what's on the floor, pick it up, drop it
  • Procedurally generated office floors, a hand-authored ground-floor lobby and a rooftop helipad finale bookend the run, with procedural offices, server rooms and security floors in between, so every run is different in the middle but consistent at the ends
  • Field of view, fog of war, and some very basic stealth, you can only see what your character can see, and the enemies have vision cones that widen and reach further once they're alerted

A couple of notes. I managed to skip a lot of the heavy lifting, rendering, map generation, even fog of war, by carrying those systems over from my previous roguelike. I've written about procedural map generation before, so I'll skip it here (though if you'd like a proper write-up, I'm happy to do one). Today we'll talk about FOV and fog.

Field of View, or: What Can You Actually See?

Fog of war is one of those features that looks trivial and absolutely is not. The question sounds simple, "which tiles can the player see right now?", but answering it correctly, every turn, for a grid full of walls and doorways, is where things get interesting.

I do a simple brute force. Each turn I walk every tile inside a circular radius around the player, and for each one I trace a straight line back to the player's eye. If nothing opaque sits on that line, the tile is visible. That's it. I know it isn't the most optimal approach, but it'll do for now. This is a "technical" blog after all, so here's how you'd do it.

#define FOV_RADIUS 8

void world_update_fov(World *world)
{
    Map *map = &world->map;

    // Anything currently lit drops to "remembered" before we recompute.
    for (int i = 0; i < map->width * map->height; i++)
        if (map->vis[i] == VIS_VISIBLE)
            map->vis[i] = VIS_SEEN;

    Vec2 eye = world_player(world)->pos;

    for (int dy = -FOV_RADIUS; dy <= FOV_RADIUS; dy++)
    {
        for (int dx = -FOV_RADIUS; dx <= FOV_RADIUS; dx++)
        {
            if (dx * dx + dy * dy > FOV_RADIUS * FOV_RADIUS)
                continue; /* keep the lit area circular */

            int tx = eye.x + dx;
            int ty = eye.y + dy;
            if (map_in_bounds(map, tx, ty) &&
                map_has_line_of_sight(map, eye, (Vec2){tx, ty}))
                map_set_vis(map, tx, ty, VIS_VISIBLE);
        }
    }
}

The whole field-of-view pass: drop last turn's visible tiles to "remembered", then relight everything in a circular radius that has clear line of sight.

The line-of-sight check itself is the part I actually enjoyed getting right. It's a Bresenham line, the same integer-only algorithm you'd use to draw a line between two pixels, walked one tile at a time from the player outward. The moment it hits an opaque tile, the target is out of sight:

bool map_has_line_of_sight(const Map *map, Vec2 a, Vec2 b)
{
    Vec2 line[256];
    int count = map_line_tiles(map, a, b, line, 256);

    // Only tiles strictly between a and b may block.
    for (int i = 0; i < count - 1; i++)
        if (map_is_opaque(map, line[i].x, line[i].y))
            return false;

    return true;
}

Line of sight, one Bresenham line at a time, if any tile between the two points is opaque, the target can't be seen.

Like I said, is this the fast way? No. Does it work? For now, yes. I've only got a handful of actors on screen at any time. Once that number creeps towards a thousand, then I'll worry about it.

Splitting the Engine From the Game

The line I mentioned earlier, between engine and game, is the decision I keep coming back to, because it changed how the whole thing feels to work on.

To be clear about what this actually is: it's not a separately compiled library with a single public header. Everything still links into one binary. The separation is by directory, bounded context and naming convention, engine/ and world/ modules on one side, gameplay/ on the other, plus the discipline to keep the dependencies pointing one way. It's a line in the codebase, not a .a file. But it turns out that's enough.

When everything lived in one big pile, adding a game feature meant wading through map generation and rendering code to get to the bit I cared about. Now there's a boundary. On one side, the engine-side modules: maps, tiles, field of view, the turn loop, input handling. On the other, the game: what a gun does, how cover works, what happens when you rescue a hostage.

The engine side exposes a fairly small, boring surface, functions that don't know anything about gameplay. A slice of it:

// a slice of the engine-side modules the game builds on

bool     map_init(Map *map, int width, int height);
bool     map_has_line_of_sight(const Map *map, Vec2 a, Vec2 b);
void     world_init(World *world);
void     world_update_fov(World *world);

Entity  *entity_create(EntityManager *manager, EntityType type, Vec2 pos);
Entity  *entity_at(EntityManager *manager, Vec2 pos);
void     entity_update(Entity *entity);

void     turn_manager_init(TurnManager *manager);
void     turn_manager_run_enemies(TurnManager *manager);

A slice of the engine-side headers. Nothing here knows what a tower or a hostage is.

The nice thing is that none of this is game specific. If this particular game idea doesn't pan out, and let's be honest, plenty of side projects don't, the plumbing underneath it is still perfectly good for the next one. If I ever do lift it out into a proper standalone library, this is the boundary I'll cut along. That's a much better outcome than another abandoned repo.

The Trade-Offs I'm Living With

I'm not going to pretend this is a polished piece of software, because it isn't. A few things I've knowingly punted on:

DecisionWhy it's fine (for now)What it'll cost later
ASCII-style glyphs (drawn via SDL2)A font and a grid, no sprite art to make, focus stays on systemsA proper tileset is a real chunk of effort
Brute-force line-of-sight FOVTrivial to reason about, and correct on the first tryShadowcasting, if the grid or radius ever grows enough to matter
No boss yetThe combat loop matters more firstThe whole game needs a climax eventually
Very basic stealthVision cones and alert states are enough to prove the conceptReal stealth AI is genuinely hard

The glyph decision is the one I'm least happy with. I couldn't find any game assets on Itch, or anywhere else, that matched the vibe I'm going for, so I suspect I'm finally going to have to learn pixel art. For now I'm rendering characters as glyphs on a grid through SDL2, there is a renderer, just a deliberately minimal one. That's not nostalgia, it's so I can answer the "how do I want this to look?" question later, once the game actually plays well. Deciding on the art before the mechanics are fun feels like painting a house before you've checked the foundations.

Where This Could Go

The honest answer is: lots of ideas, not enough executed yet. That's the eternal state of a side project.

The obvious next steps are a proper boss encounter, a deeper inventory and weapon system, and smarter enemies, ones that use cover themselves and react to being shot at rather than marching in a straight line. The stealth mechanics have the most room to grow, and probably the most potential to make the game feel distinct.

But I've got the framework now. A reusable set of engine modules, a game concept I'm actually excited about, and a week's worth of momentum.

Either way, I've had a great time. Maybe I'll keep this series going and post updates as I build it out. But whether it turns into a finished game or another entry in the graveyard of half-built roguelikes, time will tell.

Feedback and ideas are always welcome, especially from anyone who's braver with pointers than I am.

Resources

$ share