7.6 KiB
Deterrence — the feedback loop
The connection that turns the pipeline into a cycle.
This is the module that makes policing govern rather than merely clean up. Everything else in Justice reacts to one pawn: this reactor's state is the whole colony, and it feeds back into every pawn's disposition. Crime that goes unanswered emboldens everyone a little; visible justice deters everyone a little. The reason to punish is not just this prisoner — it is the message it sends the rest, and here that message is a number they all read.
The climate of order
Every map carries a MapComponent_Deterrence holding one value:
| Range | 0.0 (lawless) … 1.0 (iron) |
| Baseline | 0.5 — an ordinary colony |
| Below baseline | crime pays; dispositions run hot |
| Above baseline | order holds; dispositions run cool |
It is nudged by two events and, left alone, forgets.
The nudges
| Event | Source | Nudge | Written by |
|---|---|---|---|
| A crime, unanswered | Justice.RecordCrime(perp) |
−0.08 | Justice, Gangs (fights) |
| Visible justice done | Justice.RecordPunishment(map) |
+0.12 | Justice (Discipline.Punish) |
Both clamp the result to [0, 1]. Note the asymmetry: punishment (+0.12) outweighs crime (−0.08)
by 1.5×. A colony that answers every offence does not merely break even — it ratchets above
baseline into "order holds" territory. This is the deterrence dividend, and it is deliberate: justice
seen to be done buys more order than the crime cost.
RecordCrime also writes the record — it increments crimesCommitted and stamps lastCrimeTick on
the perpetrator — so the same event that moves the climate is the event classification and parole
later see. RecordPunishment moves only the climate; the record side of punishment (the reform
shift) is Discipline's job.
The drift
every 2500 ticks: level = MoveTowards(level, 0.5, 0.0015)
Memory fades. With nothing happening, the climate creeps back toward ordinary at 0.0015 per step, one step every 2500 ticks — which works out to:
drift per in-game day = (60000 / 2500) * 0.0015 = 24 * 0.0015 = 0.036 / day
So a single unanswered crime (−0.08) takes roughly 0.08 / 0.036 ≈ 2.2 days to heal on its own —
or one punishment (+0.12) to over-answer instantly. An iron colony you stop maintaining slides back
to baseline over about two weeks; it does not stay stern for free.
The feedback — how it reaches every pawn
This is the loop. The climate is read back into Propensity.Nurture (in Core) as a multiplier:
DeterrenceFactor(pawn) = Lerp(1.3, 0.7, level) // = 1.3 - 0.6 * level
Nurture(pawn) = ... * DeterrenceFactor(pawn)
level |
Factor | Effect on every pawn's disposition |
|---|---|---|
| 0.00 (lawless) | 1.300 | +30% hotter — crime pays, everyone feels it |
| 0.30 | 1.120 | +12% |
| 0.50 (baseline) | 1.000 | neutral — a default colony sits exactly here |
| 0.70 | 0.880 | −12% |
| 0.90 | 0.760 | −24% |
| 1.00 (iron) | 0.700 | −30% cooler — order holds, everyone calms |
Two things to notice:
- Baseline is the neutral point, by design. A default colony sits at
0.5, which is a factor of exactly1.0— ordinary order neither inflames nor calms. That symmetry is deliberate. If baseline ran hot (as an earlier tuning did), ordinary order would nudge propensity up, breed a little more crime, drop order, and feed a slow runaway. Neutral-at-baseline means only a colony that lets order slide below ordinary earns the hot multiplier, and only one that pushes above it earns the calm. - The swing is
0.7×to1.3×, symmetric around1.0— a±30%spread applied to everyone's nurture at once. This is the multiplier that makes catching one pawn matter to the disposition of the rest.
PolicingBootstrap — reconnecting the loop across the mod boundary
Core keeps Propensity.DeterrenceFactor as a neutral seam — a Func<Pawn,float> that returns 1f
until something fills it. Core alone never has to know Justice exists; that one seam is what lets Core
stay a dependency-free leaf while the deterrence loop runs through it.
At startup, PolicingBootstrap fills the seam:
Propensity.DeterrenceFactor = pawn => {
var d = pawn?.Map?.GetComponent<MapComponent_Deterrence>();
return d != null ? Mathf.Lerp(1.3f, 0.7f, d.Level) : 1f; // neutral (1.0) at baseline 0.5
};
So only when Justice is installed does the climate bend disposition; without it, Core reads a flat
1.0 and pawns are indifferent to order. This is the deterrence feedback loop, reconnected across the
mod boundary without Core ever depending on the justice layer. (If the pawn is off-map — caravan, in
transit — there is no map component, and the factor falls back to a neutral 1.0.)
Worked example — a day at the institution
The colony starts at baseline 0.5 (factor 1.05). Over one day:
| Step | Event | level |
Factor |
|---|---|---|---|
| start | — | 0.50 | 1.050 |
| 1 | Prisoner A shanks a guard (crime, unanswered) | 0.42 | 1.106 |
| 2 | Prisoner B foments trouble (crime, unanswered) | 0.34 | 1.162 |
| 3 | Warden punishes A (RecordPunishment) |
0.46 | 1.078 |
| 4 | Warden punishes B (RecordPunishment) |
0.58 | 0.994 |
| overnight | nothing happens, drift ≈ −0.036 toward 0.5 | 0.544 | 1.019 |
Two crimes cost −0.16; two punishments returned +0.24. The colony ends the day at 0.58 —
above where it started — because punishment out-answers crime. Every pawn's disposition dipped
hotter as the crimes landed (1.05 → 1.16) and then cooled below neutral once order was reasserted
(0.994). By morning the drift has begun erasing the gain, and if nothing keeps the pressure on, the
colony sinks back to its slightly-hot baseline over the next couple of weeks.
That arc — hot when crime pays, cool when justice is seen, forgetful when neither happens — is the whole reason discipline is not inert. You are not punishing a pawn; you are setting the temperature of the room.
How to play with deterrence
- Answer crime, visibly and promptly. Each punishment is worth 1.5 crimes of order. A colony that reliably punishes climbs above baseline and cools everyone; a colony that lets offences slide sinks below and heats everyone — a spiral, because hotter pawns commit more crime.
- Don't coast on a past crackdown. The climate drifts back to baseline at
0.036/day. Order is a maintenance cost, not a one-time purchase. - Push past baseline if you want calm. Neutral disposition needs
level ≈ 0.571; ordinary (0.5) still runs 5% hot. A steady rhythm of caught-and-punished offences is what holds you there. - A lawless spell is self-feeding. At
level 0.3every pawn is1.19×more disposed; more crime follows, driving the level lower still. Break the cycle with punishments, not patience.
For modders
Justice.RecordCrime(perpetrator); // -0.08 climate, +1 crime, stamps lastCrimeTick
Justice.RecordPunishment(map); // +0.12 climate
float level = map.GetComponent<MapComponent_Deterrence>().Level; // raw 0..1
RecordCrime is the writer any module calls when a pawn does something the colony would police —
Institution: Gangs calls it on rival fights, for instance. RecordPunishment is called by
Discipline.Punish. The component and statics live in the Contraband namespace (shared across the
suite since Justice's extraction from Contraband).
Part of the Institution suite for RimWorld 1.6: Core · Contraband · Policing · Corrections · Gangs · Ward.