Back to Blog
VideogamesAugust 25, 202615 min read

The Roster as a Resource: Darkest Dungeon's Economy of Scarcity

In Darkest Dungeon heroes are consumables and the hamlet is the real protagonist. A breakdown of roster management, opportunity cost, and an economy designed so you never have enough.

IM
Ignacio MelendezFull-Stack & Game Developer
The Roster as a Resource: Darkest Dungeon's Economy of Scarcity

When a hero dies in Darkest Dungeon, the game does not offer to load your save. It writes the name on a gravestone, pulls them out of the roster, and next week the stagecoach brings in two strangers. The only message you get is the Ancestor's, and he has already buried plenty of people before that one.

It is tempting to read this as aesthetic cruelty, and there is a fair amount of that. But underneath sits a very concrete economic decision: in Darkest Dungeon the heroes are not the player's character, they are the consumable the player plays with. The character is the hamlet. This article is about that inversion of roles, the roster management it produces, and an economy designed from day one so that you never have enough of anything.

This is the second article about the game. The first one covered stress and the design of attrition, which is the system feeding almost everything that follows.

1. The hamlet is the character

In a classic RPG, progress lives in the characters: you level up, you upgrade gear, and if a character dies permanently you lose progress. That is why almost no RPG has real permanent death, and the ones that do tend to push you towards reloading.

Darkest Dungeon moves permanent progress somewhere else. The hamlet upgrades (the blacksmith, the guild, the abbey, the tavern, the stagecoach, the sanitarium) are irreversible and never lost. A dead hero takes their gear, their trained skills and their quirks with them, but does not touch a single building. When any failed campaign ends, what remains is not a save file with four veterans, it is a better hamlet that will recruit better heroes.

That single architectural move solves the problem that makes permanent death unworkable in other games. Loss stops being a rollback of progress and becomes an operating cost: expensive, painful, but payable. The player can afford to lose because the long-term progress curve does not live in the thing that dies.

The design question is not "how do I make death matter", it is "where do I store progress so that death can matter without ruining the run".

In terms of data structures, the difference is literal. Persistent player state lives in the hamlet, and heroes are entities with their own lifecycle that enter and leave that state:

1public sealed class EstateState
2{
3    // Permanent progress: survives any death
4    public Dictionary<string, int> BuildingLevels { get; } = new();
5    public Wallet Wallet { get; } = new();
6    public int Week { get; private set; }
7
8    // Volatile progress: destroyed and replenished
9    public Roster Roster { get; } = new();
10    public List<string> Graveyard { get; } = new();
11
12    public void AdvanceWeek()
13    {
14        Week++;
15        Roster.RefreshRecruitPool(Week);
16    }
17
18    public void OnHeroDied(Hero hero)
19    {
20        Graveyard.Add(hero.Name);
21        Roster.Remove(hero);
22        // Deliberately: nothing in BuildingLevels is touched
23    }
24}

2. The roster as a renewable resource with a cap

The roster starts with nine slots and expands over the run to about two dozen. Every week the stagecoach drops off a handful of new candidates, practically for free, with different levels, quirks and skills. Recruiting costs almost nothing. What costs is the slot.

That cap is what turns a stream of heroes into a management system. If the roster were infinite, the player would pile up forty mediocre heroes and never make a decision: there would always be somebody rested. With a cap, every interesting recruit forces you to look at the list and ask who gets dismissed, and dismissing somebody means admitting that weeks of gold invested in them will never pay off.

1public sealed class Roster
2{
3    public int Capacity { get; private set; } = 9;
4
5    private readonly List<Hero> heroes = new();
6    private readonly List<Hero> recruitPool = new();
7
8    public bool IsFull => heroes.Count >= Capacity;
9
10    public bool TryRecruit(Hero candidate)
11    {
12        if (IsFull) return false;
13
14        recruitPool.Remove(candidate);
15        heroes.Add(candidate);
16        return true;
17    }
18
19    public void Dismiss(Hero hero)
20    {
21        // Invested gold is not refunded: it is sunk cost, and it should hurt
22        heroes.Remove(hero);
23    }
24
25    public IEnumerable<Hero> Available(int missionLevel) =>
26        heroes.Where(h => h.IsRested && h.AcceptsMission(missionLevel));
27}

The AcceptsMission method hides the most important rule in the whole system: a veteran hero refuses to go down into low-level dungeons. They cannot farm easy content, escort rookies, or act as a bodyguard. Veterans are only good for the hard content.

Full Darkest Dungeon hero roster posing in a row, with every class in the game represented
The Darkest Dungeon class lineup (Red Hook Studios). The variety is not purely aesthetic: with a cap on slots, every interesting recruit forces you to decide who gets dismissed to make room.

The consequence is that the player does not maintain a party, they maintain a farm system. They need a high-level team for the hard missions and a batch of rookies coming up underneath at the same time, because the day a veteran dies there will be no way to improvise a replacement. It is a one-line rule that multiplies the width of the management layer.

A negative restriction ("this character cannot do X") tends to generate more management than a positive reward. Forbidding veterans from farming easy content creates the need for a wide roster without having to incentivise it with artificial bonuses.

3. Opportunity cost: investing in something mortal

Recruiting is free, but a functional hero is not. Training skills at the guild, upgrading weapon and armour at the blacksmith, curing a negative quirk at the sanitarium and removing stress at the abbey all cost gold, and that gold turns into accumulated value inside an entity that can die on any expedition.

Portrait of the Darkest Dungeon Highwayman holding a lit lantern in the dark
An equipped hero in Darkest Dungeon (Red Hook Studios). Everything you see on him (weapon, armour, trained skills) is gold spent on an entity that may not come back from the next expedition.

That is the real tension in the economy. It is not "I have no gold", it is "every coin I spend on this hero is a bet that they will survive long enough to pay it back". It can be written as an expected value decision:

E[expedition]=Rpsuccesshpdeath(h)VhE[\text{expedition}] = R \cdot p_{success} - \sum_{h} p_{death}(h) \cdot V_h

Where RR is the expected reward, psuccessp_{success} the probability of completing the mission, and VhV_h the accumulated investment in each hero in the party. The interesting part is what the formula says: the more the player invests in a hero, the more expensive it becomes to risk them, so success itself keeps narrowing the room to manoeuvre. A heavily upgraded party is both the most capable and the most expensive to lose.

It is worth making that value explicit in code, even if it is never shown in the interface, because it is the number everything else is balanced against:

1public sealed class HeroInvestment
2{
3    private readonly Dictionary<CurrencyType, int> spent = new();
4
5    public void Record(CurrencyType currency, int amount)
6    {
7        spent.TryGetValue(currency, out int current);
8        spent[currency] = current + amount;
9    }
10
11    // Value in gold equivalent, for balancing and telemetry only
12    public int EstimatedValue(IReadOnlyDictionary<CurrencyType, float> weights)
13    {
14        float total = 0f;
15        foreach ((CurrencyType currency, int amount) in spent)
16            total += amount * weights[currency];
17
18        return (int)total;
19    }
20}

The classic mistake when implementing this is offering a refund when a hero is dismissed or dies, usually out of fear of frustrating the player. Returning the investment removes the decision: if the gold comes back, dismissing is free and the roster is infinite again. Sunk cost is the mechanic, not a side effect.

4. Trinkets: the only asset that can move

All the investment from the previous section has an uncomfortable property: it is non-transferable. The upgraded weapon, the armour, the trained skills and the cured quirks live inside the hero and disappear with them. Trinkets are the exception, and that is why they work as a separate economic layer.

A trinket is not bought, it is found. It comes out of dungeons, curios and bosses, it is not crafted in any building, and it can be taken off and put on another hero whenever the player wants. It is liquid capital belonging to the hamlet, not value accumulated inside a person. That liquidity is what lets the game hand out far more aggressive bonuses than gear does: a trinket can give twenty per cent extra damage in exchange for tanking stress resistance, or raise speed and lower accuracy. Because the object can move, the player is not choosing a permanent build, they are choosing what configuration to descend with this week.

Trinkets are the clearest example of why it pays to split gear into two categories: unit-bound upgrades (progress, no penalties, lost with the unit) and transferable items (power with a trade-off, they outlive the unit). The first category rewards continuity; the second generates decisions every week.

The interesting part is how the game keeps that liquidity from defusing the risk. If a hero dies, their trinkets are only recovered if the party wins the fight and there is room in the hamlet inventory, which also has a cap. If the player retreats, they go with the corpse. Equipping the best trinket in the vault on the hero going into the hardest mission is, again, a bet: the item that helps you survive the most is the one that hurts the most to lose.

1public sealed class TrinketSet
2{
3    public const int SlotCount = 2;
4
5    private readonly Trinket[] slots = new Trinket[SlotCount];
6
7    public bool TryEquip(Trinket trinket, Hero hero, int slot)
8    {
9        // Class restriction: not everything works for everyone
10        if (!trinket.AllowsClass(hero.ClassId)) return false;
11
12        slots[slot] = trinket;
13        return true;
14    }
15
16    // Trinkets transfer; the weapon, the armour and the skills do not
17    public IEnumerable<Trinket> Unequip()
18    {
19        for (int i = 0; i < SlotCount; i++)
20        {
21            if (slots[i] == null) continue;
22
23            yield return slots[i];
24            slots[i] = null;
25        }
26    }
27}
28
29public static class DeathLoot
30{
31    // Only recovered if the party holds and there is room in the hamlet
32    public static void OnHeroDied(Hero hero, CombatResult result, TrinketVault vault)
33    {
34        if (result != CombatResult.Won) return;
35
36        foreach (Trinket trinket in hero.Trinkets.Unequip())
37        {
38            if (!vault.TryStore(trinket))
39                vault.RegisterLost(trinket);   // Feeds the Shrieker's counter
40        }
41    }
42}

And here comes the only concession in the whole design. The game counts the trinkets you have lost and, once it reaches eight, the Shrieker appears: an optional boss that, on death, returns the stolen loot. It may look like a contradiction with the warning in the previous section about never refunding, but it is not, because the object is different. Gold is fungible and farmable: if you lose it, you can get more. A boss trinket is unique and may never drop again for the rest of the run. Losing it forever does not create tension, it creates a dead end, and a dead end is not a decision.

The practical rule is simple: never refund what the player can get again by playing, and always have a valve for what is irreplaceable. And if the valve exists, charge for it in the resource that is renewable (here, a week and the risk of a boss) instead of giving it away.

5. Quirks: the hero who gets more expensive on their own

Expeditions leave quirks behind. Every hero has five positive quirk slots and five negative ones, and once they fill up, each new quirk displaces one of the old ones. It is a drift system: the player chooses nothing, they just watch their heroes' sheets get dirtier expedition after expedition.

The important part is that untreated negative quirks can lock in. A locked quirk is never displaced by another one and treating it at the sanitarium becomes considerably more expensive, with a cost that also scales with the hero's level. Translated into economics: the maintenance cost of a hero grows over time, by itself, without the player making any decision, and it grows faster on the heroes they have invested the most in.

Some of those quirks are not a numeric modifier, they are a block. There are quirks that make the hero interact with curios without permission, or steal, or that keep them from using a given stress relief activity. A hero with the wrong combination can end up with no viable way to shed stress until they go through the sanitarium, which can only treat one negative quirk per hero per week. The time bottleneck from the previous section tightens exactly when it is needed most.

1public sealed class QuirkSheet
2{
3    public const int SlotsPerSign = 5;
4
5    private readonly List<Quirk> positive = new();
6    private readonly List<Quirk> negative = new();
7
8    public void Add(Quirk quirk)
9    {
10        List<Quirk> target = quirk.IsPositive ? positive : negative;
11
12        if (target.Count < SlotsPerSign)
13        {
14            target.Add(quirk);
15            return;
16        }
17
18        // Displaces the oldest one that is NOT locked; if all are, it is ignored
19        Quirk oldest = target.FirstOrDefault(q => !q.IsLocked);
20        if (oldest == null) return;
21
22        target.Remove(oldest);
23        target.Add(quirk);
24    }
25
26    // On returning from an expedition: untreated negatives can lock in
27    public void RollLocks(IRandom random)
28    {
29        foreach (Quirk quirk in negative.Where(q => !q.IsLocked))
30        {
31            quirk.WeeksUntreated++;
32            if (random.NextFloat() < quirk.LockChance)
33                quirk.IsLocked = true;
34        }
35    }
36
37    public bool BlocksActivity(string building) =>
38        negative.Any(q => q.BlockedBuildings.Contains(building));
39
40    public int TreatmentCost(Quirk quirk, int heroLevel) =>
41        quirk.BaseCost * (heroLevel + 1) * (quirk.IsLocked ? 3 : 1);
42}

The combined effect is a soft obsolescence. The game does not put an expiry date on heroes or drop their stats over time, which is what a durability system would do and would feel like an arbitrary punishment. What it does is let the cost of keeping that hero in shape rise until a new recruit, free and clean, starts to look like a better option than the veteran who needs three treatments paid for. The player reaches the conclusion that it is time to dismiss on their own, and because they did the maths, they do not read it as something the game imposed.

Be careful with the badly calibrated version of this. If maintenance grows faster than the player can pay, the system stops generating decisions and starts generating disposable rosters: everyone gets dismissed at the first bad quirk and the attachment machinery I talk about next collapses entirely. Maintenance has to be expensive, not unpayable.

6. Why punishing attachment does not feel unfair

Darkest Dungeon does something apparently contradictory: it builds attachment machinery at full speed (proper names, portraits, quirks that read as personality, nicknames the player ends up using) and then kills those characters without ceremony. It should feel like a betrayal, and it almost never does. There are three reasons, and all of them are design.

The first is that death is almost always legible in hindsight. When a hero dies, the player can reconstruct the chain: I went down with two torches, I ignored stress in the third room, I did not retreat when I should have. Randomness finishes the job, but it does not decide on its own. A lethal system needs that causal trail; without it, death reads as arbitrary and the player stops learning.

The second is that there was an alternative to dying, and it was available. Retreating, spending on provisions, rotating the roster: all of them are ways to buy safety with resources. When an exit exists and the player chose not to pay for it, the loss reads as a consequence.

The third is that the game separates the hero's drama from the player's progress, which is what the first section was about. Attachment can be intense precisely because the cost is bounded: the player allows themselves to love a mortal character because they know losing them does not invalidate the run.

This combination (high attachment, bounded cost, legible causality) is why players tell the story of their heroes' deaths as an anecdote rather than a grievance. The design goal was never for it not to hurt, it was for it to hurt productively.

7. Currencies that do not convert into each other

The hamlet economy has two layers that never touch. Gold pays for everything hero-related: provisions, training, gear, treatments, stress relief. Heirlooms (busts, portraits, deeds, crests) pay for building upgrades. They go into different pockets and, in practice, there is no free conversion between them.

It is a deliberate and very effective design decision. With a single currency, any scarcity is solved by accumulating: the player saves up and buys. With non-fungible currencies coming from different sources, scarcity becomes structural. You can have gold to spare and still be unable to expand the roster, because the building that expands the roster is not paid for with gold. The only way to get the right heirloom is to go to the type of dungeon that drops it, and that turns a purchase decision into a decision about where to play next week.

1public enum CurrencyType { Gold, Bust, Portrait, Deed, Crest }
2
3public sealed class Wallet
4{
5    private readonly Dictionary<CurrencyType, int> balances = new();
6
7    public int Get(CurrencyType currency) =>
8        balances.TryGetValue(currency, out int value) ? value : 0;
9
10    public void Add(CurrencyType currency, int amount) =>
11        balances[currency] = Get(currency) + amount;
12
13    public bool CanAfford(IReadOnlyDictionary<CurrencyType, int> cost)
14    {
15        foreach ((CurrencyType currency, int amount) in cost)
16            if (Get(currency) < amount) return false;
17
18        return true;
19    }
20
21    public bool TrySpend(IReadOnlyDictionary<CurrencyType, int> cost)
22    {
23        if (!CanAfford(cost)) return false;
24
25        foreach ((CurrencyType currency, int amount) in cost)
26            balances[currency] = Get(currency) - amount;
27
28        return true;
29    }
30
31    // There is no Exchange(): non-fungibility is the mechanic
32}
Darkest Dungeon town event: the crier announces "One Good Week", with the effect "All Idle Heroes +200% Stress Heal Received"
Weekly town event in Darkest Dungeon (Red Hook Studios). The bonus only applies to heroes who stay in the hamlet that week: even good news is charged in time.

There is a third currency the game never draws anywhere: the week. Every expedition consumes one, and a hero who spends the week shedding stress at the tavern is not available for the next mission. Since abbey and tavern slots are limited, time becomes the final bottleneck. It does not matter how much gold you have if you can only treat two heroes per week.

1public sealed class TownActivities
2{
3    private readonly Dictionary<string, int> slotsPerBuilding;
4    private readonly Dictionary<string, List<Hero>> assignments = new();
5
6    public bool TryAssign(string building, Hero hero, Wallet wallet, int cost)
7    {
8        List<Hero> queue = assignments.GetValueOrDefault(building, new List<Hero>());
9
10        if (queue.Count >= slotsPerBuilding[building]) return false;
11        if (hero.IsAssignedThisWeek) return false;
12        if (!wallet.TrySpend(new Dictionary<CurrencyType, int> { [CurrencyType.Gold] = cost }))
13            return false;
14
15        queue.Add(hero);
16        assignments[building] = queue;
17        hero.IsAssignedThisWeek = true;   // Will not join the expedition
18        return true;
19    }
20}

8. Provisions and inventory: the decision before the dungeon

Before every expedition the game shows a provisions shop: torches, food, shovels, bandages, antivenom, laudanum, keys, holy water. You buy blind, knowing the type and length of the dungeon but not what is inside it. And everything you buy takes up space in a sixteen-slot inventory that also has to hold the loot.

That is the elegant part. Provisions do not compete with gold, they compete with the reward. Bringing spare food means coming back with less treasure, and dumping food to make room for a pile of gold means the party will go hungry in the last room. The same decision shows up twice with opposite signs, once at the start and once at the end, and both are made with incomplete information.

1public sealed class ExpeditionInventory
2{
3    public const int SlotCount = 16;
4
5    private readonly List<ItemStack> stacks = new();
6
7    public int UsedSlots => stacks.Count;
8    public bool IsFull => UsedSlots >= SlotCount;
9
10    public bool TryAdd(ItemDefinition item, int quantity)
11    {
12        ItemStack existing = stacks.FirstOrDefault(s => s.Item == item && !s.IsMaxed);
13        if (existing != null)
14            return existing.TryStack(quantity);
15
16        if (IsFull) return false;   // This is where loot forces you to dump provisions
17
18        stacks.Add(new ItemStack(item, quantity));
19        return true;
20    }
21}

If you want a shop to generate real decisions, it is not enough for the items to cost money: they have to compete for limited space with the reward. An infinite inventory turns any shop into a "buy one of each" problem as soon as the player has enough gold.

9. Transferable rules

Out of the complete management loop, this is what can be taken to another game almost as is:

  • Store permanent progress outside the unit that can die. It is the prerequisite for any system with real loss. Without it, permanent death only teaches the player to reload.
  • Cap whatever accumulates. A renewable resource with no limit generates no decisions. The roster slot is worth more than the hero occupying it.
  • Forbid instead of incentivising. Veterans being unable to do the easy content forces a wide roster with no need for bonuses.
  • Do not return the investment. Sunk cost is what makes dismissing and losing mean something.
  • Separate unit-bound gear from transferable gear. The non-transferable part carries progress; the transferable part, with strong trade-offs, generates decisions every week.
  • Only refund what is irreplaceable, and charge for it separately. Losing a unique item forever does not create tension, it creates a dead end. Giving it back in exchange for time and risk is a decision.
  • Let maintenance rise on its own. It beats an expiry date: obsolescence by cost makes the player decide to retire the unit instead of the system taking it away.
  • Use non-fungible currencies with different sources. It is the cleanest way to keep scarcity from being solved by saving up.
  • Make time a currency with limited slots. The most interesting bottleneck is almost never money.
  • Make the supplies compete with the reward for the same space. That is what turns a shop into a problem.

And a general warning: this loop works because all the pieces hold each other up. Stress makes rotation necessary, rotation makes a wide roster necessary, a wide roster makes gold scarce, quirks locking in mean that gold never arrives, gold scarcity makes death painful, and the permanent hamlet makes everything above tolerable. Pulling a single piece out and gluing it into another game usually produces nothing but an annoyance. What has to be copied is not the system, it is the relationship between the systems.

Related Articles

View all articles
Darkest Dungeon's Stress: Designing for Attrition

Darkest Dungeon's Stress: Designing for Attrition

Stress is Darkest Dungeon's second health bar, and the one that actually matters. How a resource running parallel to HP turns every expedition into attrition management, and why the game is designed for you to lose.

How RNG Works in Roguelikes

How RNG Works in Roguelikes

Roguelikes depend on chance, but that chance has structure. A deep dive into seeds, determinism, and how to implement a reproducible RNG system in Unity/C#.