There is a moment in Darkest Dungeon where you realise you have been playing the wrong game for a while. You are winning every fight, nobody has dropped below half health, and the expedition is still lost: the Crusader has turned abusive and insults the party every time he acts, the Vestal is at 180 stress and the Highwayman refuses to be healed because he has decided that nothing matters anymore. Nobody has died. The party is about to collapse.
That is not an imbalance, it is the game's thesis. Darkest Dungeon splits tactical victory from strategic victory into two separate resources, and only one of them is restored for free. This article is an analysis of the second one, stress, of how it turns every expedition into attrition management, and of how to implement the idea without inheriting its rough edges.
1. HP is not the resource you manage
In most turn-based RPGs, health is the central resource: it drops during combat, you recover it with healing or rest, and the loop closes. The consequence is that a competent player can turn any fight into an arithmetic problem. If incoming damage per turn is lower than available healing per turn, the fight is already won, all that is left is executing it.
Darkest Dungeon breaks that with an almost administrative decision: when an expedition ends, hero health is restored automatically and for free, and stress is not. HP becomes a tactical resource, something that only exists inside a fight and at most inside a dungeon. Stress is the one that crosses the expedition boundary and lands on the town screen, where it is no longer healed with spells but with gold, with weeks and with limited slots at the tavern.

That is the entire structural trick, and it matters more than any specific number in the system: the resource that persists between play sessions is the one that defines the campaign's real difficulty. Anything that is restored for free when a mission ends is, by definition, a solved problem.
You can win every fight in an expedition and lose the campaign in that same expedition. The bar to watch is not the red one.
2. Two thresholds, not one bar
The stress meter runs from 0 to 200 and has exactly two points where something happens. At 100 a resolve test fires, deciding whether the hero breaks or rises. At 200 comes the heart attack: the hero drops straight to Death's Door, and if they were already there, they die.
That two-threshold structure is why the system reads well while you play. A bar that penalises continuously and proportionally (for example, "each stress point removes 0.5% accuracy") is mathematically elegant and informationally useless: the player cannot feel the difference between 61 and 68, so they stop looking at it. Two hard steps, on the other hand, turn the meter into a countdown with two marked dates, and the player starts planning around them.
The base implementation is a counter with memory of whether the test already happened, not a simple clamped sum:
1public enum StressEvent { None, ResolveTest, HeartAttack }
2
3public sealed class StressMeter
4{
5 public const float ResolveThreshold = 100f;
6 public const float BreakingPoint = 200f;
7
8 public float Value { get; private set; }
9 public bool ResolveTested { get; private set; }
10
11 public StressEvent Add(float amount)
12 {
13 if (amount <= 0f)
14 {
15 Value = Math.Max(0f, Value + amount);
16 return StressEvent.None;
17 }
18
19 Value = Math.Min(BreakingPoint, Value + amount);
20
21 if (Value >= BreakingPoint)
22 return StressEvent.HeartAttack;
23
24 if (!ResolveTested && Value >= ResolveThreshold)
25 {
26 ResolveTested = true;
27 return StressEvent.ResolveTest;
28 }
29
30 return StressEvent.None;
31 }
32}The detail that matters is ResolveTested. Without that flag, a hero hovering around 100 would fire one test after another and the system would become pure noise. With it, the first half of the bar is a warning and the second half is a slow sentence: two segments with different meanings using a single number.
The heart attack at 200 is what stops stress from being just a cumulative debuff. Without a lethal ceiling, the player would learn to ignore the bar as soon as the first threshold passed, because they would have already paid the only real cost.
3. The resolve test: affliction or virtue
On reaching 100, the game rolls the dice. Roughly three times out of four the hero suffers an affliction (paranoid, masochistic, hopeless, irrational, abusive, selfish, fearful) and one in four becomes a virtue (courageous, focused, powerful, stalwart, vigorous). Afflictions degrade stats and, above all, make the hero act on their own. Virtues do the opposite: they improve the hero and reduce party stress.
That 25% is the most underrated design piece in the whole system. An attrition system with no escape valve is simply a sentence with intermediate steps, and the player ends up disconnecting emotionally because they know how it ends. The minority chance that the worst moment of the expedition becomes the best one is what keeps the tension alive: you are not waiting for disaster, you are gambling on an outcome.
1public enum ResolveKind { Affliction, Virtue }
2
3public readonly struct ResolveOutcome
4{
5 public readonly ResolveKind Kind;
6 public readonly string Trait;
7
8 public ResolveOutcome(ResolveKind kind, string trait)
9 {
10 Kind = kind;
11 Trait = trait;
12 }
13}
14
15public sealed class ResolveTest
16{
17 private const float BaseVirtueChance = 0.25f;
18
19 private readonly WeightedTable afflictions;
20 private readonly WeightedTable virtues;
21
22 public ResolveOutcome Roll(IRandom rng, float virtueModifier)
23 {
24 float chance = Math.Clamp(BaseVirtueChance + virtueModifier, 0f, 1f);
25
26 return rng.NextFloat() < chance
27 ? new ResolveOutcome(ResolveKind.Virtue, virtues.Pick(rng))
28 : new ResolveOutcome(ResolveKind.Affliction, afflictions.Pick(rng));
29 }
30}The virtueModifier is the hook worth leaving open from the start. In the original game there are traits, camping skills and items that push that probability, and that is exactly the kind of content that makes the player feel they can fight the system instead of just suffering it. If the roll is an immutable constant, stress stops being a manageable resource and becomes weather: some days will be sunny (a virtue lands) and others stormy (and your best unit turns on the cleric).
When a system has a dramatic roll, it pays to separate probability from outcome, as here: one function decides whether something good happens and a weighted table decides what happens. That way both can be tuned separately, and the table can be filled with content without touching the maths again. It is the same principle that makes RNG systems in roguelikes manageable.
4. Contagion: why disaster cascades
An afflicted hero is not just a worse hero, they are a stress source for everyone else. They start generating party stress when they act, they cause indirect heart attacks and, in some cases, they skip their turn or attack an ally. The result is a positive feedback loop: the first affliction makes the second more likely, and the second makes the third nearly inevitable.

Many designers would treat that as a balance bug. Here it is the point. The spiral is what makes a single early mistake have visible consequences twenty minutes later, and it is what turns "this expedition is going so-so" into "this expedition has to be aborted". Without contagion, each hero would be an isolated stress container and the party would never collapse as a unit, it would only wear down in parallel.
1public sealed class AfflictionContagion
2{
3 private const float WitnessStress = 8f;
4 private const float PerTurnStress = 5f;
5
6 public void OnHeroAfflicted(Hero source, Party party)
7 {
8 // Watching a companion break already costs stress
9 foreach (Hero ally in party.Members)
10 {
11 if (ally == source || !ally.IsAlive) continue;
12 ally.Stress.Add(WitnessStress);
13 }
14 }
15
16 public void OnAfflictedTurn(Hero source, Party party, IRandom rng)
17 {
18 if (!source.HasAffliction) return;
19
20 Hero target = party.RandomLivingAlly(source, rng);
21 if (target == null) return;
22
23 target.Stress.Add(PerTurnStress * source.Affliction.ContagionScale);
24 }
25}
A spiral like this needs two things to avoid being unfair: telegraphing and an exit.
- Telegraphing means the player sees the bar climb far enough ahead to react, not that they discover the problem when it is already irreversible.
- An exit means there is a concrete action that breaks the loop (in-combat stress healing, retreat, an item). If either one is missing, the player does not perceive a system, they perceive a random punishment.
5. The torch: letting the player set their own difficulty
Most of the stress that enters an expedition does not come from monsters, it comes from the darkness. Light level drops with every move, and with less light the party takes more stress and suffers more crits, but it also finds better loot and enemies give greater rewards. The torch is an explicit dial: the player decides how much attrition to buy in exchange for how much benefit.
This solves a classic problem in difficulty design. A difficulty selector in the menu is an abstract decision made once, without information. The torch is the same decision, but made a hundred times, in context, with immediate consequences that are reversible mid-dungeon. The player does not choose "hard", they choose "I can take two more rooms like this". (And it is also a very well integrated, diegetic design decision.)
When implementing it, the important part is that darkness is a multiplier over stress sources and not a separate source, because that way it scales with all future content without touching it:
1public readonly struct StressSource
2{
3 public readonly string Id;
4 public readonly float BaseAmount;
5 public readonly bool ScalesWithDarkness;
6
7 public StressSource(string id, float baseAmount, bool scalesWithDarkness)
8 {
9 Id = id;
10 BaseAmount = baseAmount;
11 ScalesWithDarkness = scalesWithDarkness;
12 }
13}
14
15public sealed class StressPipeline
16{
17 private const float MaxDarknessBonus = 1.5f;
18
19 public float Resolve(StressSource source, float lightLevel, float stressResist)
20 {
21 float darkness = 1f - Math.Clamp(lightLevel / 100f, 0f, 1f);
22
23 float multiplier = source.ScalesWithDarkness
24 ? 1f + darkness * MaxDarknessBonus
25 : 1f;
26
27 float resisted = 1f - Math.Clamp(stressResist, 0f, 0.9f);
28 return source.BaseAmount * multiplier * resisted;
29 }
30}With that shape, the total stress of an expedition can be expressed as a budget the player administers:
Where is the base stress of each event, the normalised darkness, the maximum weight of darkness and the hero's resistance. What is interesting about writing it this way is that it shows where the player can intervene: they can reduce the number of events (shorter routes, avoiding risky curios), lower (spending torches), or raise (equipment, traits). Three distinct levers over the same resource, which is more or less the definition of a manageable system.
6. Retreat: designing partial failure
Darkest Dungeon has a retreat option that almost no tactical RPG models well: you can abandon the dungeon halfway, keep the loot gathered up to that point and return to town with the mission failed. It costs extra stress, you may lose items, and the mission still shows up as a failure. But the heroes come back alive.
That intermediate state is what makes everything above work. If the only exits from an expedition were completing it or losing the party, stress management would have no decision attached to it: you would watch the bar climb without being able to do anything about it except grit your teeth. Retreat turns the meter into a navigation instrument, because there is an action that responds to what it says.
If your game has an attrition resource, it needs an exit with partial loss. It is the design equivalent of a try/catch: without a middle route between success and catastrophe, the player can only play at never making a mistake, which is the least interesting way to play anything.
It also pays for retreat to have an explicit, readable cost: not free and not ruinous. Free means the player restarts expeditions until a good one comes up, and that destroys attrition between sessions. Ruinous means nobody uses it, and you are back to the previous problem.
7. What to copy and what not to
What is worth stealing from this system, in order of usefulness:
- A resource that survives the mission. It is the most powerful lever for making the player think in the medium term. It does not have to be stress: it can be equipment wear, debt, fatigue or reputation. What matters is that it is not restored for free on the results screen.
- Hard thresholds instead of continuous penalties. Two readable steps communicate more than a perfect curve.
- A minority escape valve. That 25% of virtues is what separates tension from fatalism.
- A risk dial in the player's hands. The torch proves that difficulty is more enjoyable when it is chosen in context, not in a menu.
- An exit with partial loss. Without it, the rest of the system is decorative.
And what should not be copied without thinking hard about it:
- The duration of the punishment. Healing stress in town costs gold, weeks and limited slots, and that is only tolerable because there is a large roster of heroes to rotate. If your game has a fixed party of four characters, the same system becomes mandatory downtime.
- The opacity of the numbers. The original game hides fairly well how much stress each thing generates. That feeds the dread, but it also makes the player learn by painful repetition instead of by deduction. It is a tone decision, not a best practice.
- The density of stress sources. Almost everything generates stress: crits taken, deaths, curios, traps, hunger, darkness. It works because the entire game is built around it. Adding an attrition resource to a game that is not produces only an annoying second bar.
The underlying lesson is that stress is not a punishment system, it is a converter: it transforms tactical performance into strategic cost, and with that it forces you to judge every fight not by whether you won it, but by what winning it cost you. It is the same idea that makes Fire Emblem's growth rates interesting, and it shows up whenever a game decides that the outcome of a battle does not fit in a boolean.


