Seventeen tutorials in, you have a dungeon you can explore, fight through, loot, descend, save, and lose. The one thing missing is a way to win. Part 17 — the finale — gives the descent a purpose: the three legendary Amulets of SDL, hidden on the deep floors. Recover all three and the dungeon yields to a victory screen. After all the systems, the win condition itself is wonderfully small — which is exactly the point of everything we built to get here.
The Amulet Is Just Another Item
We add one value to the item category enum and a maker for it:
enum class ItemCategory { Potion, Weapon, Armour, Scroll, Amulet };
Item Game::makeAmulet()
{
Item it;
it.category = ItemCategory::Amulet;
it.glyph = '"'; // the classic amulet symbol
it.color = { 255, 220, 120, 255 }; // gold
return it;
}
Then Level::generate places one Amulet on the mid-floors — depths 2, 3 and 4 (floors 3–5) — in a room well away from where the player enters, so there are exactly three to find and you must go deep to claim them:
if (depth >= 2 && depth <= 4)
{
const Room& r = map.rooms[n / 2];
Item amulet;
amulet.category = ItemCategory::Amulet;
amulet.glyph = '"'; amulet.color = { 255, 220, 120, 255 };
amulet.x = r.centreX(); amulet.y = r.centreY();
items.push_back(amulet);
}
Picking One Up = Progress (and Maybe Victory)
The existing pickup code already runs when you step onto an item. We just branch on the Amulet: it doesn't go into your pack — it advances a counter, and the third one ends the game in triumph:
if (L.items[i].category == ItemCategory::Amulet)
{
m_amulets++;
m_sound.win();
L.items.erase(L.items.begin() + i);
log("You seize an Amulet of SDL! (%d/%d)", m_amulets, AMULETS_TO_WIN);
if (m_amulets >= AMULETS_TO_WIN)
{
m_state = GameState::Won;
log("All three Amulets are yours — you have won!");
}
return;
}
GameState::Won is the fifth and final state — the title screen, play, inventory, death and now victory all live in the same little machine we started in Part 6. The HUD shows your tally (Amulets 2/3) the whole way, so you always know how close you are.
One subtlety worth seeing: grabbing the final Amulet must not then hand the monsters a free turn to kill you on your moment of triumph. So the move routine only ends the turn if the game is still being played:
// If grabbing the final amulet just won the game, don't give monsters a turn.
if (tookTurn && m_state == GameState::Playing) endPlayerTurn();
The Victory Screen
Drawn with the same framed-box helpers as the death screen, in gold instead of red. When Won, render shows it and the input loop accepts only Space (back to the hero menu for another run) or Esc:
if (m_state == GameState::Won) { drawWinScreen(); return; }
// ...and in the input loop, Dead and Won are handled together:
if (m_state == GameState::Dead || m_state == GameState::Won)
{
if (sc == SDL_SCANCODE_SPACE) { m_state = GameState::ClassSelect; dirty = true; }
if (sc == SDL_SCANCODE_ESCAPE) running = false;
break;
}
Try It — and Win
Build and run, choose a hero, and play for real this time: descend past the early floors, fight to the mid-dungeon, and watch for the gold " on depths 3, 4 and 5. Each one ticks your Amulet count up in the HUD with a triumphant chime. Take the third and the dungeon dissolves into VICTORY. It is, at last, a complete game — one with a beginning (the hero menu), a middle (the deepening dungeon), and an end you can actually reach.
The End — and Where You Go Now
From an empty SDL3 window in Part 1 to a winnable roguelike in Part 17: procedural BSP dungeons and cellular-automata caves, shadowcast field of view and fog of war, Dijkstra-map AI that hunts and flees, melee and magic, items with an identification gamble, equipment, status effects, character classes, deep scaling floors, save/load, an on-screen HUD with sound — all on a clean Game/Level/Entity architecture you refactored into shape along the way.
And it's a foundation, not a finish line. The engine is yours now: add boss monsters on the amulet floors, a shop between levels, ranged enemies, a hunger clock, a manual targeting cursor for spells, more classes, prettier tiles. Everything you'd bolt on has an obvious clean seam to hang from — which was the whole idea. Go build the world you want to build.
⚔ Thank you for playing. ⚔