Files
institution/Wiki/justice/Deterrence.md
T
flan 3530100ed5 Aggregate the whole suite wiki onto the Institution page
Wiki/Home.md is the suite's front door; every layer's wiki now mirrors here
under Wiki/<layer>/ (core, contraband, justice, gangs, ward), so the whole
suite reads in one place with the index linking to the local pages. The
layer repos stay canonical -- Tools/sync-wiki.sh refreshes the aggregated
copy on demand. The README's main page points to it.
2026-07-15 20:30:38 +00:00

169 lines
7.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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](Discipline-and-Reform.md)'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.
### JusticeBootstrap — 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, `JusticeBootstrap` fills the seam:
```csharp
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
```csharp
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 · Justice · Gangs. Developed
with substantial assistance from Claude (Anthropic).*