Files
institution/Wiki/policing/Deterrence.md
T

7.6 KiB
Raw Blame History

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 exactly 1.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× to 1.3×, symmetric around 1.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.3 every pawn is 1.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.