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.
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
# Compatibility
|
||||
|
||||
*Contraband ships zero Harmony patches. It adds defs, subclasses a work-giver, mutates two duty
|
||||
trees at startup, and polls. That is why it composes instead of colliding.*
|
||||
|
||||
## Requires: Institution: Core
|
||||
|
||||
Contraband declares a hard `modDependency` on **`flan.institution.core`** and loads after it. This
|
||||
is not optional — after the split, the entire substrate Contraband reads lives in Core:
|
||||
|
||||
| Core provides | Contraband uses it for |
|
||||
|---|---|
|
||||
| `SecuredContext` / `SecuredContexts` / `CanConceal` / `IsHeld` | who counts as a concealer; who can be intaken, whittle, or dig |
|
||||
| `Propensity.Would(pawn, base, salt)` | every disposition roll — intake (0.6), improvise (0.10), dig (0.08), all seeded and scaled by nature × nurture |
|
||||
| `CriminalRecord` + `GameComponent_CriminalRecords` | `timesSearched`, `timesCaught`, `escapeAttempts`, `contrabandMade` — written by search and acquisition, read by priority |
|
||||
|
||||
Install Contraband without Core and it will not run. Everything else on this page is optional
|
||||
interplay that degrades gracefully when the other mod is absent.
|
||||
|
||||
## Zero Harmony patches
|
||||
|
||||
Post-split, the mod's one Harmony patch (the regime that forces Joy for prisoners) moved to
|
||||
**Institution: Justice**, so Contraband is Harmony-free again. Instead of patching, it:
|
||||
|
||||
- **Adds a new `WorkGiver`** (`WorkGiver_Warden_Search`) rather than patching a vanilla one.
|
||||
- **Subclasses `WorkGiver_Warden`**, inheriting its behaviour rather than overriding it.
|
||||
- **Mutates two `DutyDef` think trees at startup** via `StaticConstructorOnStartup` (not Harmony) —
|
||||
an XML patch on a duty think tree silently no-ops in 1.6, so it is done in C#, deterministically.
|
||||
- **Polls** every held pawn from a `MapComponent` for intake, improvisation, and tunnels — no hook
|
||||
on the capture event is needed because the tick already visits every held pawn.
|
||||
|
||||
The net effect: the surfaces other prison mods touch are mostly untouched here, so conflicts are the
|
||||
exception, not the rule.
|
||||
|
||||
## Foul Play — the carboy bridge
|
||||
|
||||
**Foul Play** (the "Piss Nuke" mod) stays its own mod with its own front door, and yet its **carboy
|
||||
is a first-class contraband item.** This is the flagship demonstration of Contraband's *"items are
|
||||
defs, subsystems are classes, mods are audiences"* principle, and it works through two seams:
|
||||
|
||||
1. **Cross-assembly worker resolution.** `CompProperties_Contraband`'s `useWorker` / `escapeWorker`
|
||||
fields are plain `Type`s, and RimWorld resolves def `Type` fields with
|
||||
`GenTypes.GetTypeInAnyAssembly` — searching **every loaded assembly**. So the carboy declares
|
||||
`CompProperties_Contraband` and points `useWorker` at a class that lives in *Foul Play's* assembly
|
||||
(e.g. a throw-the-carboy worker). Contraband never has to know that class exists.
|
||||
2. **The opaque content tag.** A carboy is not just "a jar" — it is a jar *of* a fermented blend, and
|
||||
that blend has to survive being concealed. Foul Play sets Contraband's two delegates:
|
||||
|
||||
```csharp
|
||||
ContrabandUtility.ContentTagOf = thing => /* serialise the carboy's blend */;
|
||||
ContrabandUtility.ApplyContent = (thing, tag) => /* pour that blend back in on redeem */;
|
||||
```
|
||||
|
||||
When a carboy is intaken or smuggled, Contraband captures the tag blind; on redemption it hands
|
||||
the tag back to Foul Play to restore the contents. **Contraband knows nothing about substances or
|
||||
fermentation** — that framework lives entirely in Foul Play. If Foul Play is absent, both delegates
|
||||
are null, no content is captured, and nothing breaks: a jar is just a jar. See
|
||||
[Concealment and Intake](Concealment-and-Intake.md).
|
||||
|
||||
The dependency runs one way only: Foul Play bridges *to* Contraband (and to Core) if they are
|
||||
present; Contraband takes **no** dependency on Foul Play.
|
||||
|
||||
## Institution: Justice — the record is the shared bus
|
||||
|
||||
Contraband and **Justice** never call each other. They meet on Core's `CriminalRecord`:
|
||||
|
||||
```
|
||||
Contraband writes ──▶ CriminalRecord ──▶ Justice reads
|
||||
timesSearched (in Core) Classification (security grade)
|
||||
timesCaught Deterrence (colony-wide signal)
|
||||
escapeAttempts Discipline / Parole (reform)
|
||||
contrabandMade
|
||||
```
|
||||
|
||||
- **Catching contraband feeds classification.** A prisoner repeatedly caught with shivs or foiled
|
||||
mid-tunnel accrues `timesCaught` and `escapeAttempts`, which Justice's classification weighs into a
|
||||
higher security grade.
|
||||
- **And it feeds back.** Justice's deterrence signal flows *back* through Core's propensity seam
|
||||
(`Nurture`), nudging every future intake / improvise / dig roll a Contraband pawn makes. A prison
|
||||
that catches and disciplines becomes a prison where fewer prisoners bother trying — without either
|
||||
mod referencing the other. Run Contraband alone and the deterrence factor is a neutral 1.0; the
|
||||
record fields still route the warden.
|
||||
|
||||
## Institution: Gangs — the contraband economy
|
||||
|
||||
**Gangs** depends on Core, **Contraband**, and Justice, because a gang *is* a contraband economy:
|
||||
|
||||
- Gang smuggling moves contraband through the same public `Conceal` door intake and corruption use.
|
||||
- A gang's outside members are the natural caller for the **reach-in corruption** primitive
|
||||
(`Corruption.Smuggle`) — paying a bent warden to make a delivery. That primitive is exposed in
|
||||
Contraband but has no in-repo trigger; Gangs is what drives it. See [Corruption](Corruption.md).
|
||||
- Gang fights route to Justice's crime record, which then raises search priority here. The loop
|
||||
closes through Core again.
|
||||
|
||||
## Prison mods the suite is tested against
|
||||
|
||||
| Mod | Interaction | Verdict |
|
||||
|---|---|---|
|
||||
| **Prison Commons** | postfixes `WorkGiver_Warden.ShouldSkip` on the **base** class; Contraband's search subclasses it and does not override, so searches respect prison-commons/allowed areas **for free** | works by inheritance |
|
||||
| **Custom Prisoner Interactions** | patches only the *named* vanilla warden givers (`_Chat`, `_Convert`, `_Enslave`, `_ReleasePrisoner`); it cannot see a brand-new giver | no conflict |
|
||||
| **Prisoner Realism** | its escapes are for "mood prisoners" mid-break; a tunnel breakout hands off to vanilla `PrisonBreakUtility.StartPrisonBreak`, which Prisoner Realism layers on. Tunnels **complement** it — they let ward patients and slaves who would never mood-break still, patiently, dig out | complementary |
|
||||
| **Prison Labor** | prisoners working outside the cell are still held pawns and still tracked; Contraband adds no patches to labor jobs. A work assignment is simply another place a disposed prisoner might come by materials | no conflict |
|
||||
| **Prisoner Recreation** | overlaps the *regime* feature — which now lives in **Institution: Justice**, not here, and is written to be idempotent with Prisoner Recreation | not Contraband's concern post-split |
|
||||
|
||||
## Load order
|
||||
|
||||
```
|
||||
Ludeon.RimWorld
|
||||
flan.institution.core ← required, loads before Contraband
|
||||
flan.institution.contraband ← this mod
|
||||
(Foul Play / Justice / Gangs — any order after Core; each bridges if present)
|
||||
```
|
||||
|
||||
## The general rule
|
||||
|
||||
If a mod adds prisoner behaviour by **patching vanilla warden givers or the escape duty tree by
|
||||
name**, it will not see Contraband's additions, and Contraband will not see its — they pass each
|
||||
other. If a mod adds a *new* `WorkGiver` or duty node of its own, it stacks. The one place to watch is
|
||||
another mod that *also* rewrites the same two duty think trees (`PrisonerEscape`,
|
||||
`PrisonerAssaultColony`) wholesale; Contraband inserts its nodes at a specific anchor and logs a
|
||||
warning if that anchor is missing, so a load-order or conflict problem is visible in the log rather
|
||||
than silent.
|
||||
|
||||
---
|
||||
|
||||
*Part of the **Institution** suite — Core · Contraband · Justice · Gangs, alongside Foul Play and
|
||||
Ward. Each stands alone; together they interlock.*
|
||||
*Developed with substantial assistance from Claude (Anthropic).*
|
||||
@@ -0,0 +1,230 @@
|
||||
# Concealment and Intake
|
||||
|
||||
*How a hidden item exists without existing, how it gets onto a prisoner in the first place, and how
|
||||
it becomes real again.*
|
||||
|
||||
This is the spine of the whole mod. Everything else — shivs, tunnels, searches, corruption — is a
|
||||
different way to add to, or remove from, the private list this page describes.
|
||||
|
||||
## While it is concealed, there is no Thing
|
||||
|
||||
The single most important rule, and the source states it plainly:
|
||||
|
||||
> `MapComponent_Contraband` tracks what each pawn is hiding. **Concealed contraband is not a Thing.
|
||||
> It is a secret.** It is not in the gear tab, there is no icon, and the player is told nothing —
|
||||
> because an item you can see in a UI is not contraband, it is inventory, and there would be nothing
|
||||
> for a warden to discover and no reason to ever order a search.
|
||||
|
||||
Concretely, a concealed item lives as a `ConcealedItem` row in a `List` on the map component. It
|
||||
carries the def, a `progress` value, the material it ended up made of, an optional source it is
|
||||
being harvested from, tunnel state if it is a pick, and an opaque `contentTag`. It holds **no
|
||||
spawned object** anywhere in the world until one of two things happens:
|
||||
|
||||
| Moment | What happens | Method |
|
||||
|---|---|---|
|
||||
| The prisoner **uses** it | It materialises into their inventory as a real `Thing` | `TryRedeem` |
|
||||
| A warden **finds** it | It is deleted from the list; it never becomes a `Thing` at all | `Confiscate` |
|
||||
|
||||
A corollary the search page leans on hard: **an empty search must still cost time.** If the
|
||||
`Search` job only ever appeared when there was genuinely something to find, the mere *offer* of the
|
||||
job would be the discovery. You have to be able to toss a clean cell and come up with nothing.
|
||||
|
||||
## Where a thing can hide: `ConcealSite`
|
||||
|
||||
Every contraband def declares where it can be hidden. This is not decoration — it decides **who can
|
||||
ever find it**.
|
||||
|
||||
```csharp
|
||||
[Flags] enum ConcealSite { None = 0, OnBody = 1, InCell = 2 }
|
||||
```
|
||||
|
||||
| Site | Holds | Found by |
|
||||
|---|---|---|
|
||||
| `OnBody` | small: a shiv, pills, a phone | searching the **pawn** |
|
||||
| `InCell` | bulky: a rifle, a carboy, a crude pick | tossing the **room** |
|
||||
|
||||
A warden turning over a prisoner and their cell reaches both (`OnBody | InCell`). The reason the
|
||||
enum is split at all: a **constable** in Justice's now-shipping policing layer, frisking a colonist
|
||||
named in a street crime, reaches only `OnBody`, so whatever is under their floorboards stays there
|
||||
until somebody has grounds to search the *room*. The split between "who may search" and "what a
|
||||
search reaches" is deliberate — see [Warden Search](Warden-Search.md).
|
||||
|
||||
## Three sources of contraband, and no list of items
|
||||
|
||||
At startup, `ContrabandUtility` scans every `ThingDef` and decides whether it is contraband. There
|
||||
is no hardcoded list of item names — there are three recognisers:
|
||||
|
||||
1. **Explicit.** Any def carrying `CompProperties_Contraband`. This is the opt-in, and it is how a
|
||||
mod plugs in its own `acquireWorker` / `useWorker` / `escapeWorker` behaviour.
|
||||
2. **Every drug** (`ThingDef.IsDrug`) — inferred automatically.
|
||||
3. **Every weapon** (`ThingDef.IsWeapon`) that is not `destroyOnDrop` — inferred from **mass**.
|
||||
|
||||
The inference (`ContrabandUtility.Infer`) fills in concealability and hiding place so a modder never
|
||||
has to:
|
||||
|
||||
| Kind | Test | `concealability` | `hideIn` |
|
||||
|---|---|---|---|
|
||||
| Hard drug | `drugCategory == Hard` | **0.75** | `OnBody \| InCell` |
|
||||
| Other drug | any other `IsDrug` | **0.5** | `OnBody \| InCell` |
|
||||
| Light weapon | `Mass ≤ 2.0` | **0.7** | `OnBody \| InCell` |
|
||||
| Heavy weapon | `2.0 < Mass ≤ 10.0` | **0.35** | `InCell` only |
|
||||
| Very heavy weapon | `Mass > 10.0` | — | **not contraband** (a minigun hides nowhere) |
|
||||
|
||||
`concealability` runs 0..1 where **0 = a warden finds it every time and 1 = never**; higher is
|
||||
harder to find. Drugs and inferred weapons get **no `acquireWorker`** (you cannot synthesise yayo in
|
||||
a bare cell, and you cannot conjure a rifle) and **no `useWorker`** — because vanilla already knows
|
||||
what to do with them. `JobGiver_SatisfyChemicalNeed` takes a drug; an armed prisoner *is* the
|
||||
payload. Getting the item into the cell is the entire problem, and that is all these routes solve.
|
||||
|
||||
Nothing here mutates another mod's defs. It is a lookup keyed on the def, computed once, held in a
|
||||
dictionary. Modded drugs and weapons are covered without ever being named.
|
||||
|
||||
## The poll: no Harmony
|
||||
|
||||
`MapComponent_Contraband` runs on a heartbeat. Every **250 ticks** (`CheckIntervalTicks`) it visits
|
||||
every pawn who could be concealing something and does all the ongoing work: intake, tunnel progress,
|
||||
and improvisation. If nothing on the map is self-acquirable and there are no escape tools registered,
|
||||
it returns immediately and costs nothing.
|
||||
|
||||
Polling — rather than patching — is a deliberate architecture choice. **The tick already visits
|
||||
every held pawn**, so intake needs no Harmony hook on the capture event: a pawn who is in custody but
|
||||
not yet in the `intakeProcessed` set is, by definition, one who was newly taken. The whole mod ships
|
||||
zero Harmony patches, and this is a large part of why.
|
||||
|
||||
### Who counts as a concealer
|
||||
|
||||
`ContrabandUtility.Concealers(map)` is **not** just prisoners. It delegates to Core's
|
||||
secured-context predicate and yields every pawn whose belongings are anyone's business — prisoners,
|
||||
slaves, **and free colonists**:
|
||||
|
||||
> A free colonist forbidden their drug by a drug policy or an ideoligion is precisely the pawn who
|
||||
> keeps a private stash — and vanilla already denies them, with no valve.
|
||||
|
||||
Whether anyone may *search* a given concealer is a separate question with a separate answer (the
|
||||
warden's authority reaches prisoners; a future cop's reaches colonists). Tracking is pawn-agnostic;
|
||||
authority is not.
|
||||
|
||||
## Intake: the moment of capture
|
||||
|
||||
Vanilla never strips a captive *on capture* — a downed pawn keeps whatever they walked in with, and
|
||||
the warden path just hauls them to a cell. Intake is Contraband's answer to that gap.
|
||||
|
||||
`RunIntake(pawn)` runs from the poll, on the first pass that sees a newly-held pawn:
|
||||
|
||||
1. **Held only.** The pawn must be humanlike and `IsHeld` (prisoner, slave, ward patient). A free
|
||||
colonist is a concealer, but nobody frisks and processes *them* on the way into a cell, so their
|
||||
carried gear is never intaken.
|
||||
2. **Once.** The pawn's `thingIDNumber` is added to a `HashSet<int>`. The set stores an `int`, not a
|
||||
pawn reference, so it holds no ghost of a captive who has since died. A second pass finds them
|
||||
already processed and does nothing.
|
||||
3. **Even while downed.** Intake runs *before* the "must be awake" gate. The stash a captive walked
|
||||
in with is hidden the moment they are in custody, not when they wake — everything *after* intake
|
||||
(whittling, digging) needs them awake, but hiding what is already on you does not.
|
||||
4. **Only what can ride on a body.** `IsIntakeConcealable` accepts a thing only if its contraband
|
||||
props include the `OnBody` flag. A shiv up a sleeve is intaken; a rifle or a suit of armour is
|
||||
too bulky to palm and stays as ordinary strippable gear the player confiscates for free.
|
||||
5. **Per-item disposition.** For each concealable thing the captive carries, the mod rolls whether
|
||||
*this* pawn would hide *this* item:
|
||||
|
||||
```
|
||||
Propensity.Would(pawn, 0.6, SaltIntake ^ item.thingIDNumber)
|
||||
```
|
||||
|
||||
Base chance 0.6, then scaled by the pawn's nature × nurture (Core's `Propensity`). The roll is
|
||||
**seeded per (pawn, item)** — a fixed fact, not a fresh coin each tick. The frightened cook
|
||||
surrenders the knife; the disposed lifer palms it, and always would have.
|
||||
|
||||
When a pawn does hide an item, the real `Thing` is **destroyed** (equipment via
|
||||
`DestroyEquipment`, inventory via `Destroy(Vanish)`) and a concealed secret is created in its place,
|
||||
preserving what the item was made of (`stuff`) and — critically — its `contentTag`.
|
||||
|
||||
## The content tag: preserving contents without understanding them
|
||||
|
||||
A concealed vessel is not just "a jar" — it is a jar *of* something (hooch, piss, a fermented
|
||||
blend), and that something has to survive being hidden and come back on redemption. Contraband
|
||||
stores a single opaque string, `contentTag`, and **has no idea what it means**:
|
||||
|
||||
```csharp
|
||||
// A bridge mod (Foul Play) fills these in. Null (no bridge) = items have no contents.
|
||||
ContrabandUtility.ContentTagOf; // Func<Thing, string> — read on conceal
|
||||
ContrabandUtility.ApplyContent; // Action<Thing, string> — applied on redeem
|
||||
```
|
||||
|
||||
On concealment, `ContentTagOf(thing)` captures the tag (e.g. "a jar of a piss+hooch cocktail"). On
|
||||
redemption, `ApplyContent(newThing, tag)` pours that content back into the freshly-made thing. This
|
||||
is the exact seam **Foul Play** uses to keep a smuggled carboy's contents intact. Without a bridge,
|
||||
both delegates are null, no content is captured, and nothing is lost — a shiv is just a shiv.
|
||||
|
||||
Fermentation itself lives in Foul Play, not here. A concealed vessel materialises **fresh** and
|
||||
ferments afterward like any other, which suits the theme: fresh-brewed piss is weak, and time is
|
||||
what makes it a weapon. (`concealedTick` records how long it was hidden, for any bridge that wants to
|
||||
restore pre-aging — Contraband keeps the number but never acts on it.) See
|
||||
[Compatibility](Compatibility.md).
|
||||
|
||||
## Redemption: the secret becomes a Thing
|
||||
|
||||
`TryRedeem(pawn, def)` is where a secret turns real:
|
||||
|
||||
1. Choose the material — the harvested `stuff`, or the def's default if it was made from stuff.
|
||||
2. `ThingMaker.MakeThing(def, stuff)`.
|
||||
3. If there was a `contentTag`, call `ApplyContent` to restore the contents.
|
||||
4. `TryAdd` it to the pawn's inventory. If that fails, the thing is vanished and nothing is lost.
|
||||
5. Remove the secret from the list.
|
||||
|
||||
The trigger for redemption is the breakout duty tree. `ThinkTreeInjection` runs once at startup and
|
||||
mutates two duty defs directly (an XML patch on a `DutyDef` think tree silently no-ops in 1.6, so it
|
||||
is done in C#):
|
||||
|
||||
| Duty | Injected | Where |
|
||||
|---|---|---|
|
||||
| `PrisonerEscape` | `JobGiver_ArmSelf`, then `JobGiver_UseContraband` | **after** `JobGiver_TakeCombatEnhancingDrug` |
|
||||
| `PrisonerAssaultColony` | `JobGiver_ArmSelf`, then `JobGiver_UseContraband` | **before** `JobGiver_AIFightEnemies` |
|
||||
|
||||
`JobGiver_UseContraband` sits right beside `JobGiver_TakeCombatEnhancingDrug` — the one contraband
|
||||
hook Ludeon wrote and then left reading an always-empty inventory. For each thing a pawn is hiding:
|
||||
|
||||
- **Inert contraband** (a drug, a stashed rifle — no `useWorker`): just materialise it and get out
|
||||
of the way. Vanilla takes the drug via `JobGiver_SatisfyChemicalNeed`; a weapon in inventory is a
|
||||
weapon. But a stashed **weapon** only comes out for someone who would actually use it
|
||||
(`Disposition.WouldArmSelf`) — the same person who would not stop to pick a rifle off the floor
|
||||
does not become a fighter because the knife was in their sock, and a pawn incapable of violence
|
||||
never does at all. If they qualify, the weapon is moved from inventory into their equipment slot;
|
||||
otherwise it stays hidden and they run. See [Improvised Weapons](Improvised-Weapons.md).
|
||||
- **Active contraband** (a `useWorker` — e.g. Foul Play's throw-the-carboy): materialise it, then
|
||||
hand off to the worker's `TryGiveJob`, which produces the actual use job.
|
||||
|
||||
## Confiscation and the door every route comes through
|
||||
|
||||
`Confiscate(pawn, def)` simply removes the secret. It **never becomes a Thing** — the warden found
|
||||
it, and there is nothing to drop on the floor. (This is why searching is not a way for the colony to
|
||||
*acquire* a prisoner's drugs; it is a way to *deny* them.)
|
||||
|
||||
At the other end, `Conceal(prisoner, def, ...)` is the public door **every** supply route comes
|
||||
through — intake calls it, a bent warden calls it ([Corruption](Corruption.md)), and a smuggling
|
||||
gangmate ([Institution: Gangs](Compatibility.md)) calls it. It hands a prisoner something already
|
||||
finished and hidden, optionally with a `contentTag`. One door, many keys.
|
||||
|
||||
## Persistence
|
||||
|
||||
`MapComponent_Contraband.ExposeData` deep-saves the secret list, the per-pawn search cooldowns, and
|
||||
the `intakeProcessed` set. On load it prunes any row whose pawn or def failed to resolve — prisoners
|
||||
who died or left take their secrets with them, and a null key would throw on the next tick.
|
||||
|
||||
## Quick reference
|
||||
|
||||
| Constant | Value | Meaning |
|
||||
|---|---|---|
|
||||
| `CheckIntervalTicks` | 250 | poll cadence for intake / tunnels / improvisation |
|
||||
| Intake base chance | 0.60 | per-item, before nature × nurture |
|
||||
| Intake gate | `IsHeld` + `OnBody` | only held pawns, only body-hideable items |
|
||||
| Hard-drug concealability | 0.75 | inferred |
|
||||
| Other-drug concealability | 0.50 | inferred |
|
||||
| Light-weapon concealability | 0.70 | `Mass ≤ 2.0`, hides on body |
|
||||
| Heavy-weapon concealability | 0.35 | `2.0 < Mass ≤ 10.0`, `InCell` only |
|
||||
| Unhideable mass | > 10.0 | not contraband |
|
||||
|
||||
---
|
||||
|
||||
*Part of the **Institution** suite — Core · Contraband · Justice · Gangs, alongside Foul Play and
|
||||
Ward. Each stands alone; together they interlock.*
|
||||
*Developed with substantial assistance from Claude (Anthropic).*
|
||||
@@ -0,0 +1,141 @@
|
||||
# Corruption
|
||||
|
||||
*A search is only as thorough as the warden running it, and a warden is a person with their own
|
||||
diligence and their own price.*
|
||||
|
||||
The counter to contraband is the [warden search](Warden-Search.md) — but a search is not a machine.
|
||||
It is a human being with traits and a mood, and Contraband models that human being on **two axes,
|
||||
both read off the same vanilla traits, mood, and relationships — no new stat to maintain**:
|
||||
|
||||
| Axis | Question | Effect on a search |
|
||||
|---|---|---|
|
||||
| **Diligence** | How hard do they look? | multiplies the find chance |
|
||||
| **Corruption** | How bent are they? | a flat chance to look away — and a route *into* the prison |
|
||||
|
||||
This is *"what turns 'the game vs the player' into 'the player's own guards, each with an angle.'"*
|
||||
The prisoner you can control. The guard you assigned to watch them, you cannot.
|
||||
|
||||
## Diligence — how hard they look
|
||||
|
||||
`WardenDisposition.Diligence(warden)` returns 0..1 and multiplies the whole find chance in
|
||||
`JobDriver_SearchPawn`. A lax or miserable warden misses things a dutiful one would turn up.
|
||||
|
||||
```
|
||||
d = 0.55 // baseline
|
||||
d += 0.1 × degreeOf(Industriousness) // Lazy lowers, Industrious raises (per degree)
|
||||
d += 0.15 if Abrasive // no qualms turning a cell over
|
||||
d -= 0.20 if Kind // a gentle hand
|
||||
d += 0.10 if Psychopath // does not care how it feels
|
||||
d ×= Lerp(0.6, 1.1, mood) // a miserable warden phones it in
|
||||
d = clamp01(d)
|
||||
```
|
||||
|
||||
| Trait / factor | Δ Diligence | Reasoning |
|
||||
|---|---|---|
|
||||
| Industrious | +0.1 per degree | a hard worker searches hard |
|
||||
| Lazy | −0.1 per degree | a lazy one frisks gently |
|
||||
| Abrasive | +0.15 | happy to toss a cell |
|
||||
| Psychopath | +0.10 | thoroughness without squeamishness |
|
||||
| Kind | −0.20 | reluctant to be rough |
|
||||
| Mood | ×0.6 (miserable) .. ×1.1 (content) | morale is thoroughness |
|
||||
|
||||
## Corruption — their price
|
||||
|
||||
`WardenDisposition.Corruption(warden)` returns 0..1. In a search, a bent warden gets a flat chance
|
||||
to **look away** on each item they would otherwise have a shot at finding:
|
||||
|
||||
```
|
||||
lookAwayChance = Corruption(warden) × 0.6 // rolled per hidden item, before the find roll
|
||||
```
|
||||
|
||||
```
|
||||
c = 0.04 // baseline — most guards are mostly honest
|
||||
c += 0.40 if Greedy // has a price
|
||||
c += 0.15 if Kind // bends the rules out of sympathy
|
||||
c -= 0.10 if Ascetic // wants nothing, cannot be bought
|
||||
c -= 0.05 if Abrasive
|
||||
c ×= Lerp(1.6, 0.7, mood) // a desperate, miserable warden is more temptable
|
||||
c = clamp01(c)
|
||||
```
|
||||
|
||||
| Trait / factor | Δ Corruption | Reasoning |
|
||||
|---|---|---|
|
||||
| Greedy | +0.40 | the classic bent guard — everyone has a number |
|
||||
| Kind | +0.15 | looks the other way out of sympathy, not money |
|
||||
| Ascetic | −0.10 | wants nothing, so cannot be bought |
|
||||
| Abrasive | −0.05 | too surly to do anyone a favour |
|
||||
| Mood | ×1.6 (miserable) .. ×0.7 (content) | a happy guard is harder to tempt |
|
||||
|
||||
## The Kind warden is a double liability
|
||||
|
||||
Read the two formulas together and one trait jumps out. **Kind** *lowers* Diligence (−0.20) **and**
|
||||
*raises* Corruption (+0.15). The gentle guard both frisks softly and bends the rules out of pity —
|
||||
the worst warden you can put on a contraband detail, and the least obvious one, because "kind" reads
|
||||
as a virtue everywhere else in the game. Meanwhile **mood** cuts the same way on both axes: a
|
||||
miserable warden looks *less* hard and is *more* temptable. A depressed guard is a sieve at both
|
||||
ends.
|
||||
|
||||
## Worked examples
|
||||
|
||||
Find chance for a hidden item is `baseFind × Diligence`, with a separate `lookAway = Corruption ×
|
||||
0.6` chance to ignore it entirely. Assume a mid-skill warden whose raw find chance on a shiv is
|
||||
around 0.30 before disposition:
|
||||
|
||||
| Warden | Diligence | Corruption | Look-away/item | Net on a 0.30 shiv |
|
||||
|---|---|---|---|---|
|
||||
| Neutral, content | ~0.61 | ~0.03 | ~2% | thorough-ish: ~0.18 find |
|
||||
| Abrasive + Psychopath, content | ~0.88 | ~0.02 | ~1% | a nightmare for prisoners: ~0.26 find |
|
||||
| Greedy, content | ~0.61 | ~0.31 | ~18% | looks capable, pockets bribes |
|
||||
| Greedy, **miserable** | ~0.37 | ~0.62 | ~37% | ignores contraband over a third of the time |
|
||||
| Kind + **miserable** | ~0.25 | ~0.27 | ~16% | a sieve at both ends: ~0.07 find |
|
||||
|
||||
(Numbers rounded; mood taken as content ≈ 100% or miserable ≈ 20%.) The takeaway is that **who you
|
||||
assign to search matters as much as whether you search at all.** A greedy, miserable warden is not a
|
||||
guard — he is a supply line you are paying to pretend otherwise.
|
||||
|
||||
## The bent warden as a supply route *in*
|
||||
|
||||
Corruption is not only "looks away." The `Corruption.Smuggle` primitive is the reaching-*in* half — a
|
||||
guard who slips a prisoner contraband through the very same `Conceal` door a smuggler or a gangmate
|
||||
uses:
|
||||
|
||||
```csharp
|
||||
ThingDef Corruption.Smuggle(warden, prisoner, tracker)
|
||||
// picks a random OnBody-hideable contraband and Conceal()s it onto the prisoner; returns the def
|
||||
```
|
||||
|
||||
Only what can ride on a body gets slipped across a handshake (`OnBody`). The method is the **act
|
||||
itself**; the caller decides *who* and *when*, gated on `WardenDisposition.Corruption`. In Contraband
|
||||
on its own, this primitive is exposed but not yet wired to an in-game trigger — it is the seam
|
||||
**Institution: Gangs** drives when a gang's outside members pay a bent warden to make a delivery, and
|
||||
the thing a planned secure-zone checkpoint that frisks *guards* is meant to catch. Search-side
|
||||
corruption (looking away) is fully live today; reach-in corruption is the hook the rest of the suite
|
||||
plugs into. See [Compatibility](Compatibility.md).
|
||||
|
||||
## How to play with it
|
||||
|
||||
- **Vet your wardens.** A Greedy or Kind pawn on prison duty is a liability the trait tooltip won't
|
||||
warn you about. Ascetic and Industrious make the best searchers; Psychopath and Abrasive are
|
||||
brutally effective if you can stomach them.
|
||||
- **Watch mood.** A prison-duty warden in a slump is failing at both jobs — searching *and* not
|
||||
smuggling. Keep your guards content or rotate them out.
|
||||
- **Redundancy beats brilliance.** Because each search is one shot per prisoner per day and even a
|
||||
good warden misses a well-hidden shiv, a *second* diligent warden over time matters more than a
|
||||
single perfect search.
|
||||
|
||||
## Quick reference
|
||||
|
||||
| Constant | Value | Meaning |
|
||||
|---|---|---|
|
||||
| Diligence baseline | 0.55 | before traits and mood |
|
||||
| Corruption baseline | 0.04 | most guards are mostly honest |
|
||||
| Diligence mood scale | ×0.6 .. ×1.1 | miserable → content |
|
||||
| Corruption mood scale | ×1.6 .. ×0.7 | miserable → content |
|
||||
| Look-away multiplier | ×0.6 | applied to Corruption, per item |
|
||||
| Smuggle payload | `OnBody` contraband only | what fits in a handshake |
|
||||
|
||||
---
|
||||
|
||||
*Part of the **Institution** suite — Core · Contraband · Justice · Gangs, alongside Foul Play and
|
||||
Ward. Each stands alone; together they interlock.*
|
||||
*Developed with substantial assistance from Claude (Anthropic).*
|
||||
@@ -0,0 +1,130 @@
|
||||
# Institution: Contraband
|
||||
|
||||
*Prisoners get hold of things they should not have. Wardens turn the cell over looking for them.*
|
||||
|
||||
This is the player-facing wiki for **Institution: Contraband**, a RimWorld 1.6 mod. It documents
|
||||
every mechanic with the real numbers pulled from the source, explains why each one works the way it
|
||||
does, and tells you how to play with them — and against them.
|
||||
|
||||
> **Note on scope.** Contraband was recently *split*. It is now one thing: the **physical smuggling
|
||||
> loop** of a prison. The engine it used to carry — propensity, the criminal record, the
|
||||
> secured-context predicate — now lives in **Institution: Core**, which this mod requires. The
|
||||
> response systems (classification, deterrence, discipline, parole, regime) went to **Institution:
|
||||
> Justice**, and the gang network to **Institution: Gangs**. If this page mentions "the record" or
|
||||
> "a pawn's propensity," those are Core's; Contraband reads them.
|
||||
|
||||
## The one idea
|
||||
|
||||
The whole Institution suite rests on a single sentence:
|
||||
|
||||
> **Every pawn has a criminal propensity on a spectrum — nature (traits) × nurture (circumstance,
|
||||
> mood, treatment) — and the colony can police it.**
|
||||
|
||||
Contraband is the half of that sentence you can *hold in your hand*. A propensity is abstract until
|
||||
a disposed prisoner files a blade off his bunk, palms a twist of yayo on the way into a cell, or
|
||||
starts digging. Contraband is where the spectrum becomes an object with edges.
|
||||
|
||||
## Vanilla built the demand and forgot the supply chain
|
||||
|
||||
RimWorld already ships a fully-built junkie. Raiders spawn addicted (`chemicalAddictionChance`,
|
||||
`forcedAddictions`) and carrying combat drugs (`combatEnhancingDrugsChance`). You down one, capture
|
||||
strips everything (`DropAndForbidEverything`), and now withdrawal grips them in the cell
|
||||
(`Need_Chemical`, `Hediff_Addiction`). A prisoner already *seeks* a drug on their own —
|
||||
`JobGiver_SatisfyChemicalNeed` is in the prisoner think tree — and already *ignores* the forbidden
|
||||
flag inside their own cell (`ForbidUtility.CaresAboutForbidden`).
|
||||
|
||||
Put a drug within reach of an addicted prisoner and the base game makes them take it. Today. With no
|
||||
new code. The consumer, the craving, the reach, the ignore-forbidden — all of it ships.
|
||||
|
||||
What does **not** ship is any answer to two questions:
|
||||
|
||||
| Vanilla has | Vanilla lacks |
|
||||
|---|---|
|
||||
| A prisoner who wants contraband | Any route for contraband to reach the cell |
|
||||
| A warden with fifteen jobs | A sixteenth called *search* |
|
||||
|
||||
Search `Assembly-CSharp.dll` for `Contraband`, `Search`, `Confiscate`, `Smuggle`, `Frisk`,
|
||||
`Shakedown` and you get **zero types**. The warden can chat, convert, feed, execute, enslave,
|
||||
release, suppress — and cannot look in a pocket. Contraband builds exactly the missing two halves:
|
||||
**supply** and **search**. It does not need to build the junkie, because Ludeon already did.
|
||||
|
||||
## Concealment is a secret, not an item
|
||||
|
||||
This is the load-bearing design decision, inherited from Foul Play (the Piss Nuke) and kept:
|
||||
|
||||
> **While it is concealed, there is no `Thing`.**
|
||||
|
||||
A hidden shiv is not in the gear tab. There is no icon. There is no stack on a shelf. The player is
|
||||
told **nothing**. The item exists only as a row in `MapComponent_Contraband`'s private list, and it
|
||||
becomes a real `Thing` at exactly two moments:
|
||||
|
||||
- when the prisoner **uses** it (the secret is "redeemed" into their inventory), and
|
||||
- when a warden **finds** it in a search (it is confiscated and never becomes a thing at all).
|
||||
|
||||
Why go to the trouble? Because *an item you can see in a UI is not contraband* — it's inventory, and
|
||||
there would be nothing to discover and no reason to ever order a search. The whole tension of the
|
||||
system is that **you never know**. A warden you send to toss a cell might find a blade, might foil a
|
||||
tunnel, or might waste twenty minutes on a prisoner who was hiding nothing — and that empty search
|
||||
still has to cost, or the mere *offer* of the job would be the discovery.
|
||||
|
||||
Read the full lifecycle in **Concealment and Intake**.
|
||||
|
||||
## The four supply routes and the one counter
|
||||
|
||||
| Route | What it is | Page |
|
||||
|---|---|---|
|
||||
| **Intake** | A captive hides the small contraband they walked in with | [Concealment and Intake](Concealment-and-Intake.md) |
|
||||
| **Improvise** | Whittle a shiv from the cell's own furniture | [Improvised Weapons](Improvised-Weapons.md) |
|
||||
| **Tunnel** | Dig out under the perimeter with a filed pick | [Tunnels](Tunnels.md) |
|
||||
| **A bent warden** | A greedy or over-kind guard smuggles *for* a prisoner | [Corruption](Corruption.md) |
|
||||
| **The counter** | A prioritized cell toss that reads the tells | [Warden Search](Warden-Search.md) |
|
||||
|
||||
And because every guard is a person with their own diligence and their own price, the search is only
|
||||
as good as who you send — see **Corruption**.
|
||||
|
||||
## How everything is contraband without a list
|
||||
|
||||
Contraband names no items. It recognises three sources at load and logs the count:
|
||||
|
||||
1. **Anything carrying `CompProperties_Contraband`** — the explicit opt-in, with pluggable behaviour.
|
||||
2. **Every drug** (`ThingDef.IsDrug`) — vanilla's, Vanilla Expanded's, mods that don't exist yet.
|
||||
Hard drugs conceal better (0.75 vs 0.5).
|
||||
3. **Every weapon** (`ThingDef.IsWeapon`) — concealability and hiding place derived from **mass**. A
|
||||
knife rides on the body; a heavy gun goes under the floor; a minigun (over 10 kg) hides nowhere.
|
||||
|
||||
It is a lookup, not a patch. Nothing mutates another mod's defs. Details in
|
||||
[Concealment and Intake](Concealment-and-Intake.md) and [Compatibility](Compatibility.md).
|
||||
|
||||
## Index
|
||||
|
||||
- **[Concealment and Intake](Concealment-and-Intake.md)** — the secret model, `ConcealSite`, the
|
||||
three sources, intake on capture, redemption, and the opaque content-tag bridge.
|
||||
- **[Improvised Weapons](Improvised-Weapons.md)** — the shiv whittled from furniture, material
|
||||
carry-through, the damage tell, and who reaches for it in a breakout.
|
||||
- **[Tunnels](Tunnels.md)** — the Prison-Architect dig under the perimeter, which pawns dig, and how
|
||||
a search uncovers the shaft.
|
||||
- **[Warden Search](Warden-Search.md)** — the prioritized search, how tells and the record drive
|
||||
priority, what a search does, and how it writes back.
|
||||
- **[Corruption](Corruption.md)** — Diligence and Corruption from traits and mood, how they scale a
|
||||
find, and the bent warden who looks away or smuggles in.
|
||||
- **[Compatibility](Compatibility.md)** — the Core dependency, the Foul Play bridge, Justice/Gangs
|
||||
interplay, and the prison mods the suite is tested against.
|
||||
|
||||
## The suite
|
||||
|
||||
Each mod stands alone as an install; together they form one system.
|
||||
|
||||
| Mod | Owns | Depends on |
|
||||
|---|---|---|
|
||||
| **Institution: Core** | propensity, criminal record, secured context | base game |
|
||||
| **Institution: Contraband** (this) | concealment, intake, shivs, tunnels, search, corruption | Core |
|
||||
| **Institution: Justice** | classification, deterrence, discipline, parole, regime | Core |
|
||||
| **Institution: Gangs** | gangs as contraband economies | Core, Contraband, Justice |
|
||||
| **Foul Play** | vessel + substance + throw framework; the Piss Nuke; bridges its carboy in as contraband | standalone |
|
||||
| **Ward** | a ward prison mode + the suite's test harness | — |
|
||||
|
||||
---
|
||||
|
||||
*Part of the **Institution** suite — Core · Contraband · Justice · Gangs, alongside Foul Play and
|
||||
Ward. Each stands alone; together they interlock.*
|
||||
*Developed with substantial assistance from Claude (Anthropic).*
|
||||
@@ -0,0 +1,203 @@
|
||||
# Improvised Weapons
|
||||
|
||||
*The acquisition route you cannot deny — because you are obliged to furnish the cell, and the
|
||||
furniture is the raw material.*
|
||||
|
||||
You have to give a prisoner a bed. RimWorld will nag you until you do. That bed is a frame of wood,
|
||||
or steel, or — if you built a nice prison — plasteel. Give a disposed prisoner a season of
|
||||
unsupervised time with it and they will file a blade off it. **The prison's own furnishings are the
|
||||
arsenal, and better furniture is worse.**
|
||||
|
||||
## The shiv
|
||||
|
||||
`CB_Shiv` is a crude stabbing weapon: *"A blade filed off a bed frame and wrapped in cloth for a
|
||||
grip. Barely a weapon — but a barely-a-weapon in a cell you thought was empty is a dead guard."*
|
||||
|
||||
| Stat | Value |
|
||||
|---|---|
|
||||
| Tech level | Neolithic |
|
||||
| Mass | 0.4 kg |
|
||||
| `concealability` | **0.75** (hides very well — it rides on the body) |
|
||||
| `hideIn` | `OnBody \| InCell` |
|
||||
| Point (Stab) | power 7, cooldown 1.5 s |
|
||||
| Handle (Blunt) | power 5, cooldown 1.6 s |
|
||||
| `acquireWorker` | `AcquireWorker_Improvise` |
|
||||
| `useWorker` | none — an armed prisoner *is* the payload |
|
||||
|
||||
It is **stuffable** (`stuffCategories`: Metallic, Woody, Stony), which is the entire point of the
|
||||
harvest mechanic: a shiv's stats come from its material for free. A wood shiv is feeble; a plasteel
|
||||
one is vicious. You never furnished a cell thinking of it as an armoury, but that is what it is.
|
||||
|
||||
## Who whittles: the disposition gate
|
||||
|
||||
`AcquireWorker_Improvise.CanAcquire` is not "any prisoner." Three things must all be true:
|
||||
|
||||
1. **Held.** The pawn is a prisoner, slave, or ward patient (`IsHeld`). Free colonists do not whittle
|
||||
shivs in the commons — *yet* (the concealment layer already tracks them; only the authority to act
|
||||
on them is missing).
|
||||
2. **Disposed.** A seeded propensity roll:
|
||||
|
||||
```
|
||||
Propensity.Would(pawn, 0.10, SaltImprovise)
|
||||
```
|
||||
|
||||
Base chance **0.10**, scaled by Core's nature × nurture. It is seeded per pawn, so it is a *fixed
|
||||
fact* about them — the mistreated, high-propensity lifer chews his bed apart; the frightened cook
|
||||
never does, and never would have, no matter how many times you reload.
|
||||
3. **Material in reach.** There is a workable source (see below).
|
||||
|
||||
The base is deliberately low. Most prisoners are not making weapons. When one is, that is a fact
|
||||
about *who they are and how you keep them*, and the tell it leaves is meant to point you at exactly
|
||||
that pawn.
|
||||
|
||||
## The source: what counts as raw material
|
||||
|
||||
`FindSource` looks for the nearest reachable, damageable, usefully-stuffed piece of furniture:
|
||||
|
||||
- **Their own assigned bed first** — it is right there in the cell.
|
||||
- Otherwise the **closest artificial building within 12 tiles** that qualifies.
|
||||
|
||||
`IsWorkableSource` requires all of:
|
||||
|
||||
| Requirement | Why |
|
||||
|---|---|
|
||||
| Made of `Stuff` | you cannot whittle a blade out of nothing |
|
||||
| `useHitPoints` | it has to be damageable |
|
||||
| Stuff category is **Metallic, Woody, or Stony** | cloth and leather cannot be filed into a blade |
|
||||
| Reachable at `Touch` through `Danger.Deadly` | they have to get to it |
|
||||
|
||||
This is also the counter-play. A cell furnished only in cloth and hydroponics offers nothing to file.
|
||||
A steel bed offers a steel shiv. **What you build the cell out of decides whether it can become a
|
||||
weapon, and how nasty that weapon is.**
|
||||
|
||||
## Harvest by damage: the tell *is* the mechanic
|
||||
|
||||
Each poll the pawn makes progress, `OnHarvestTick` chips at the source:
|
||||
|
||||
```
|
||||
damage = max(1, round(source.MaxHitPoints × 0.012)) // Blunt, ~1.2% of max HP per worked poll
|
||||
```
|
||||
|
||||
The damage is not a side effect — it is the point. A gnawed bed is **probable cause**. The same
|
||||
poll that advances the shiv damages the furniture, and that visible wear is what
|
||||
[Warden Search](Warden-Search.md) reads to send a guard to the *right* prisoner first: a badly
|
||||
damaged bed shoots a prisoner to the top of the search priority list. Whittling a shiv is loud, in
|
||||
the only sense that matters — it leaves a mark you can act on.
|
||||
|
||||
Because the source takes real damage, a determined prisoner can **reduce a piece of furniture to
|
||||
wreckage and move on to the next**: if the source is destroyed before the shiv is finished,
|
||||
`FindSource` looks for another, and if there is none, the route **stalls** (progress is kept, not
|
||||
lost). A materially poor cell is a real defence — the prisoner cannot make progress with nothing to
|
||||
file.
|
||||
|
||||
## How long, and the material it becomes
|
||||
|
||||
Progress uses the default rate — roughly **1.5 unsupervised days** of active work — jittered ±25%
|
||||
each poll so no two shivs finish on the same schedule:
|
||||
|
||||
```
|
||||
progressPerPoll = 250 / (1.5 × 60000) × Rand.Range(0.75, 1.25)
|
||||
```
|
||||
|
||||
When progress reaches 1, the shiv is finished, **silently** — the player is told nothing. Two things
|
||||
happen:
|
||||
|
||||
- **Material carry-through.** `item.stuff = source.Stuff`. A wood bed yields a wood shiv, a plasteel
|
||||
bunk a plasteel one. When the shiv is later redeemed into a real `Thing`, it is made of exactly
|
||||
that material, and its combat stats follow.
|
||||
- **The record.** `CriminalRecord.contrabandMade` is incremented (Core's record). That number feeds
|
||||
the warden's future suspicion of this pawn and, if you run **Institution: Justice**, their security
|
||||
classification.
|
||||
|
||||
## The shiv comes out in a break — and who reaches for it
|
||||
|
||||
A hidden shiv is inert until a breakout. When a prisoner enters the `PrisonerEscape` or
|
||||
`PrisonerAssaultColony` duty, `JobGiver_UseContraband` offers to materialise what they are hiding —
|
||||
but a **weapon** only comes out for someone who would actually use it. That test is
|
||||
`Disposition.WouldArmSelf`, and it is the same gate `JobGiver_ArmSelf` uses to decide whether an
|
||||
escapee even bothers to pick a weapon off the floor.
|
||||
|
||||
This matters because **vanilla escapees never arm themselves at all.**
|
||||
`JobGiver_PickUpOpportunisticWeapon` exists and is simply absent from the escape duty tree, so a
|
||||
base-game prisoner walks out unarmed, always. Arming is genuinely new behaviour, so it is kept rare
|
||||
enough that when it happens you recognise *who* did it.
|
||||
|
||||
### `WouldArmSelf`
|
||||
|
||||
First a **hard gate**: a pawn who `WorkTagIsDisabled(Violent)` never arms, ever — a pacifist who
|
||||
picks up a rifle is not a rare event, it is a bug. Otherwise:
|
||||
|
||||
```
|
||||
chance = ArmDisposition(pawn) × ArmCircumstance(pawn)
|
||||
armed = WouldSeeded(pawn, min(chance, 0.85), SaltArm) // seeded — a fixed fact per pawn
|
||||
```
|
||||
|
||||
**Disposition** — who they are, fixed, nothing about today changes it:
|
||||
|
||||
```
|
||||
ArmDisposition = 0.10 (base) × trait × skill
|
||||
```
|
||||
|
||||
| Factor | Value |
|
||||
|---|---|
|
||||
| Bloodlust | ×2.5 (highest applicable trait only — not a stack) |
|
||||
| Psychopath | ×2.0 |
|
||||
| Brawler | ×1.8 |
|
||||
| Wimp | ×0.3 |
|
||||
| Kind | ×0.4 |
|
||||
| skill | `0.5 + max(Shooting, Melee)/20 × 1.5` → range **0.5 .. 2.0** |
|
||||
|
||||
*Being captured in a raid does not make someone a fighter* — plenty of prisoners are cooks and
|
||||
conscripts. A weapon is only worth grabbing if you know what to do with it, hence the skill term.
|
||||
|
||||
**Circumstance** — what is happening right now, live, not seeded:
|
||||
|
||||
| Condition | Effect | Reasoning |
|
||||
|---|---|---|
|
||||
| Health < 50% | ×0.4 | badly hurt people run or crawl; they don't seek a fight |
|
||||
| ≥ 2 other prisoners escaping within 12 tiles | ×2.0 | a crowd emboldens; one man slips out, six men riot |
|
||||
| An armed colonist within 20 tiles | ×1.8 | now a weapon is not bravado, it's the plan |
|
||||
|
||||
### Which weapon they reach for
|
||||
|
||||
`JobGiver_ArmSelf` prefers, in order:
|
||||
|
||||
1. **Their own dropped weapon** — the gun they carried when you downed them, still lying where it
|
||||
fell within 45 tiles. Vanilla remembers it in `Pawn_MindState.droppedWeapon` and then never uses
|
||||
the field for prisoners. *"A man walking back to the spot where his own rifle fell is a better
|
||||
story than a man rummaging through your stockpile."* A prisoner does not care that you marked it
|
||||
forbidden.
|
||||
2. **Anything to hand** within 18 tiles — but only for a pawn over a higher disposition floor
|
||||
(`WouldSeeded(pawn, 0.35, …)`). Rummaging a stockpile is a further step than grabbing your own gun
|
||||
back.
|
||||
|
||||
A concealed shiv slots into this the natural way: a prisoner who has been sitting on a blade and who
|
||||
is the sort to use it does not leave it in his pocket. `JobGiver_UseContraband` redeems it and moves
|
||||
it straight from inventory into the equipment slot. The pawn who would *not* arm keeps it hidden and
|
||||
runs — and the shiv survives to be found in a later search, or used in a later break.
|
||||
|
||||
## Not only shivs: the crude pick
|
||||
|
||||
`AcquireWorker_Improvise` is shared. The **crude pick** (`CB_DiggingTool`) is filed off furniture
|
||||
exactly the same way — same disposition gate, same damage tell — but it carries an `escapeWorker`
|
||||
instead of weapon stats, and its purpose is a tunnel rather than a fight. See [Tunnels](Tunnels.md).
|
||||
|
||||
## Quick reference
|
||||
|
||||
| Constant | Value | Meaning |
|
||||
|---|---|---|
|
||||
| Improvise base chance | 0.10 | before nature × nurture; seeded per pawn |
|
||||
| Damage per worked poll | ~1.2% of source max HP | Blunt; the visible tell |
|
||||
| Source search radius | 12 tiles | own bed first, then nearest workable furniture |
|
||||
| Time to finish a shiv | ~1.5 active days | jittered ±25% per poll |
|
||||
| Workable stuff | Metallic / Woody / Stony | cloth and leather cannot be whittled |
|
||||
| Shiv `concealability` | 0.75 | hard to find on the body |
|
||||
| Arm chance cap | 0.85 | after disposition × circumstance |
|
||||
| Own-weapon reach | 45 tiles | their dropped weapon |
|
||||
| Any-weapon reach | 18 tiles | scavenge floor 0.35 disposition |
|
||||
|
||||
---
|
||||
|
||||
*Part of the **Institution** suite — Core · Contraband · Justice · Gangs, alongside Foul Play and
|
||||
Ward. Each stands alone; together they interlock.*
|
||||
*Developed with substantial assistance from Claude (Anthropic).*
|
||||
@@ -0,0 +1,178 @@
|
||||
# Tunnels
|
||||
|
||||
*The Prison-Architect dig: a disposed pawn goes down, not through — under the ground and everything
|
||||
built on it, out past the last wall you were relying on.*
|
||||
|
||||
A locked door stops a prisoner who wants to walk out. It does nothing at all about a prisoner who
|
||||
digs. This is the escape that ignores your perimeter, because it does not cross it — it goes beneath
|
||||
it. It is slow, it is quiet, and it works for pawns who would never join a mood-driven riot.
|
||||
|
||||
## The crude pick
|
||||
|
||||
The dig needs a tool. `CB_DiggingTool` is a crude pick, filed off the cell's own furniture exactly
|
||||
the way a shiv is (`AcquireWorker_Improvise` — same disposition gate, same damage-to-furniture
|
||||
tell; see [Improvised Weapons](Improvised-Weapons.md)). Where the shiv is a weapon, this is a *way
|
||||
out*: it carries no combat stats and is never equipped.
|
||||
|
||||
| Stat | Value |
|
||||
|---|---|
|
||||
| Description | *"Useless as a weapon and useless above ground — but pointed downward, patiently, it is a way out that no lock and no guard can see."* |
|
||||
| Max HP | 60 |
|
||||
| Mass | 1.2 kg |
|
||||
| `concealability` | **0.5** |
|
||||
| `hideIn` | `InCell` only (too bulky to ride on a body) |
|
||||
| `acquireWorker` | `AcquireWorker_Improvise` |
|
||||
| `escapeWorker` | `EscapeTunnelWorker` |
|
||||
| Deterioration | 2.0 |
|
||||
|
||||
Because it hides `InCell` only, a warden turning over the *room* can find the pick itself — and, far
|
||||
more easily, the hole it is digging.
|
||||
|
||||
## A second secret riding on the first
|
||||
|
||||
A tunnel is *"the third thing a contraband item can be, after 'made' and 'used in a break': a thing
|
||||
you dig your way OUT with, quietly, over days."* It is deliberately **not a Job**. There is no
|
||||
supervised task a warden could walk in on and interrupt — only a secret that grows and a wall that
|
||||
eventually gives. Progress is driven from `MapComponent_Contraband`'s poll (`TickTunnels`), the same
|
||||
250-tick heartbeat that makes contraband, so *the same search that finds a shiv can find the tunnel*.
|
||||
|
||||
The pick must first be **made** (or smuggled). Only once it is finished (`Ready`) does the dig begin,
|
||||
as a second, longer secret carried on the same `ConcealedItem` (`escapeProgress`, `escapeWall`).
|
||||
|
||||
## Who digs
|
||||
|
||||
`EscapeTunnelWorker.WouldDig` gates on three things:
|
||||
|
||||
1. **Humanlike and held** — prisoner, slave, or ward patient (`IsHeld`, via Core's secured context).
|
||||
2. **The disposition to dig for weeks:**
|
||||
|
||||
```
|
||||
Propensity.Would(holder, 0.08, SaltTunnel)
|
||||
```
|
||||
|
||||
Base chance **0.08** — deliberately below the shiv's 0.10. *"Fewer pawns will dig for weeks than
|
||||
will palm a shiv."* Seeded per pawn, so it is a fixed fact about them.
|
||||
3. **A viable plan exists** — there has to be somewhere to surface (see below).
|
||||
|
||||
This is the entire reason tunnels live in Contraband and not in a prisoner-only escape mod:
|
||||
|
||||
> It works for prisoners **and ward patients** alike. Prisoner Realism's escapes are for "mood
|
||||
> prisoners" mid-break bashing a door, and a committed ward patient is never that pawn. A patient
|
||||
> with a filed-down pick and the disposition to use it can still, slowly, dig out — and nothing else
|
||||
> in the ecosystem lets them.
|
||||
|
||||
## Planning the dig: aim for the wilderness
|
||||
|
||||
The target is **not** the cell wall. *"You do not go through the wall, you go under it."* `TryPlan`
|
||||
finds the **nearest wilderness** — the first standable cell, scanning radially outward from the
|
||||
holder up to **60 tiles** (`MaxSearchRadius`), whose room *touches the map edge* and is not the room
|
||||
they are held in.
|
||||
|
||||
Two consequences fall straight out of that:
|
||||
|
||||
- **Depth is your defence, and a single wall is not.** A tunnel from a deep bunker is a long dig; a
|
||||
tunnel from a shack against the map edge is a short one. Wrapping one more wall around a cell does
|
||||
almost nothing — the dig already ignores intervening walls entirely.
|
||||
- **A fully enclosed, map-locked base with no reachable edge has nowhere to surface.** `TryPlan`
|
||||
fails, `ProgressPerCheck` returns 0, and the tunnel **stalls** — progress is kept, not lost. Seal
|
||||
the map and the shaft simply waits.
|
||||
|
||||
### How long
|
||||
|
||||
Dig time scales with how deep the cell sits:
|
||||
|
||||
```
|
||||
requiredDays = clamp(distanceToWilderness / 6, 3, 15) // CellsPerDay = 6
|
||||
progressPerPoll = 250 / (requiredDays × 60000) × Rand.Range(0.75, 1.25)
|
||||
```
|
||||
|
||||
| | Days |
|
||||
|---|---|
|
||||
| Minimum (shallow cell near the edge) | **3** |
|
||||
| Maximum (deep bunker) | **15** |
|
||||
| Per day of digging | ~6 cells of depth |
|
||||
|
||||
The floor of 3 days keeps even the shortest tunnel a *real threat window* you have time to catch; the
|
||||
ceiling of 15 keeps the deepest from being effectively forever.
|
||||
|
||||
## The tell: spoil
|
||||
|
||||
A dig has to go somewhere, and the dirt is where you notice it. Each dig tick, with a 50% seeded
|
||||
chance (`OnDigTick`), the worker drops one tile of `Filth_Dirt` at the holder's position:
|
||||
|
||||
> A dirt floor swallows it; a paved cell shows it — exactly where a player might notice.
|
||||
|
||||
It is cheap and occasional on purpose: enough that a paved cell slowly accumulates a suspicious mess,
|
||||
not so much that it spams filth or litters a clean cell every tick. If you floor your cells, spoil is
|
||||
a genuine visual cue that someone is digging.
|
||||
|
||||
## Finding a tunnel in a search
|
||||
|
||||
A tunnel is *"a second, far less hideable secret."* The pick has `concealability` 0.5, but the
|
||||
**shaft** gives itself away in proportion to how far along it is. When a warden search reaches the
|
||||
`InCell` site, `ExtraDiscoverChance` is added on top of the tool's normal find chance:
|
||||
|
||||
```
|
||||
extraChance = clamp01(escapeProgress) × 0.4 // up to +0.40 at a nearly-finished tunnel
|
||||
```
|
||||
|
||||
So a routine cell toss that finds a shiv can also catch a dig in progress — *"the same guard patrol
|
||||
that finds a shiv finds the hole"* — and the deeper the shaft, the harder it is to miss. A foiled
|
||||
dig is logged the same as one that ran: on discovery, `CriminalRecord.escapeAttempts` is
|
||||
incremented, which raises this pawn's future search priority sharply (a known tunneller is the
|
||||
prisoner a warden checks first — see [Warden Search](Warden-Search.md)). The full find math is on the
|
||||
[Warden Search](Warden-Search.md) page.
|
||||
|
||||
## Breakout
|
||||
|
||||
When `escapeProgress` reaches 1, `OnBreakout` fires:
|
||||
|
||||
1. **Re-plan the exit** so it is correct even if the base changed during the weeks of digging.
|
||||
2. **Breach the outer wall or fence** the tunnel comes up beside (`LastContainmentOnLine` — the
|
||||
*outermost* impassable edifice or fence between the cell and the surfacing point, walking inward
|
||||
from the exit). It is destroyed with `KillFinalize` — a visible hole the colony must repair, and
|
||||
proof of how they got out. An open perimeter needs no breach.
|
||||
3. **Record the attempt** (`escapeAttempts++`) — it happened whether or not they get clear of the
|
||||
map.
|
||||
4. **Surface beyond the perimeter.** They dug all the way out, so they emerge *outside*, not in the
|
||||
yard: the pawn is teleported to the exit cell, with a little spoil dirt to mark where they came
|
||||
up.
|
||||
5. **Hand off to vanilla** (`PrisonBreakUtility.StartPrisonBreak`). Now that they are loose and
|
||||
outside, the base game's prison-break flow — and anything layered on it, e.g. **Prisoner
|
||||
Realism** — takes it from there. A pawn already outside who no longer qualifies as an in-prison
|
||||
escapee simply flees on their own; the breach and the emergence are the real outcome regardless.
|
||||
|
||||
The player gets one message: *"{pawn} has tunnelled out under the perimeter and escaped."* The tunnel
|
||||
and the pick are spent together — the `ConcealedItem` is removed.
|
||||
|
||||
## How to defend against it
|
||||
|
||||
| Defence | Effect |
|
||||
|---|---|
|
||||
| **Floor the cells** | spoil dirt becomes visible; you can *see* a dig |
|
||||
| **Search known offenders** | a logged escape attempt is the heaviest single term in search priority |
|
||||
| **Deny the pick's raw material** | no workable furniture, no pick — same starve as the shiv |
|
||||
| **Depth** | a deeper cell is a longer dig and a wider window to catch it |
|
||||
| **Seal the map** | no reachable edge means the tunnel stalls indefinitely |
|
||||
|
||||
Note what does **not** help much: adding perimeter walls. The dig goes under them and only breaches
|
||||
the *last* one on the way out.
|
||||
|
||||
## Quick reference
|
||||
|
||||
| Constant | Value | Meaning |
|
||||
|---|---|---|
|
||||
| Dig base chance | 0.08 | before nature × nurture; seeded per pawn |
|
||||
| Wilderness search radius | 60 tiles | how far out it looks for a surfacing point |
|
||||
| Cells per day | 6 | dig speed vs. cell depth |
|
||||
| Min / max dig time | 3 / 15 days | clamps on `distance / 6` |
|
||||
| Spoil chance per dig tick | 50% | one tile of `Filth_Dirt` |
|
||||
| Extra find chance | up to +0.40 | scales with `escapeProgress` |
|
||||
| Pick `concealability` | 0.5 | `InCell` only |
|
||||
| On discovery / breakout | `escapeAttempts++` | logged either way |
|
||||
|
||||
---
|
||||
|
||||
*Part of the **Institution** suite — Core · Contraband · Justice · Gangs, alongside Foul Play and
|
||||
Ward. Each stands alone; together they interlock.*
|
||||
*Developed with substantial assistance from Claude (Anthropic).*
|
||||
@@ -0,0 +1,198 @@
|
||||
# Warden Search
|
||||
|
||||
*The sixteenth warden job vanilla never wrote. A guard turns the cell over, and might find nothing —
|
||||
which is exactly why it has to cost.*
|
||||
|
||||
The warden can chat, convert, feed, execute, enslave, release, suppress, and eleven other things.
|
||||
Searching a prisoner is not one of them, and no such verb exists anywhere in the base game.
|
||||
Contraband adds it as a **new** `WorkGiver`, not a patch on a vanilla one, and builds it around a
|
||||
single hard rule: **the search must be able to come up empty.**
|
||||
|
||||
## Authority vs. act
|
||||
|
||||
The design splits one job into two classes:
|
||||
|
||||
| Class | Question it answers | Notes |
|
||||
|---|---|---|
|
||||
| `WorkGiver_Warden_Search` | **Who** may be searched? | subclasses `WorkGiver_Warden` — a warden already has the run of the prison, so no new authority is invented |
|
||||
| `JobDriver_SearchPawn` | **What** is a search? | takes any pawn, searches any pawn |
|
||||
|
||||
> The WorkGiver is the authority. This driver is just the act.
|
||||
|
||||
Why bother splitting them? Because the enforcement layer is meant to grow. A future **Institution:
|
||||
Police** module adds a *second* authority — a cop who may stop and frisk a **free colonist** — as a
|
||||
new WorkGiver that reuses the same driver with a **narrower `Reach`** (a street frisk gets what is
|
||||
`OnBody`, not what is under the floorboards). That module is an *addition*, not a rewrite, precisely
|
||||
because "who may search" and "what a search reaches" were never welded together.
|
||||
|
||||
## The WorkGiver: who gets searched
|
||||
|
||||
`Contraband_WardenSearch` is a `Warden` work-type giver:
|
||||
|
||||
| Field | Value | Why |
|
||||
|---|---|---|
|
||||
| `workType` | Warden | it is warden work |
|
||||
| `priorityInType` | 40 | **below feeding** — a starving prisoner matters more than a hidden shiv |
|
||||
| `requiredCapacities` | Manipulation, Sight | you search with your hands and eyes |
|
||||
| `Prioritized` | **true** (in C#) | rank candidates by suspicion, not by distance |
|
||||
|
||||
`Prioritized` has to be set in code: in 1.6 it is a `WorkGiver_Scanner` property, **not** a
|
||||
`WorkGiverDef` field, and an XML `<prioritized>` silently errors on load. That flag is what lets a
|
||||
gnawed bed and a thick record send the warden to the *right* prisoner first instead of the nearest.
|
||||
|
||||
`JobOnThing` will offer a search only if all of these hold: the warden should take care of this
|
||||
prisoner, the target is a `Pawn`, the per-prisoner cooldown is up (`CanSearchNow`), the prisoner is
|
||||
**awake, not downed, and not in a mental state**, and the warden can reserve them.
|
||||
|
||||
Note what is **not** checked — and the source is emphatic about it:
|
||||
|
||||
> Whether the prisoner is actually hiding anything. **The warden does not know. Nobody knows until
|
||||
> the cell is turned over.** If the job were only offered when there was something to find, the mere
|
||||
> appearance of the job would BE the discovery, and searching would stop being a gamble the player
|
||||
> pays for in warden-hours. An empty search has to be possible, and has to cost.
|
||||
|
||||
### The cooldown
|
||||
|
||||
`MapComponent_Contraband` won't let the same prisoner be turned over more than **once per day**:
|
||||
|
||||
```
|
||||
SearchCooldownTicks = 60000 // one in-game day
|
||||
```
|
||||
|
||||
You cannot brute-force a well-hidden shiv by re-searching the same prisoner ten times in an hour.
|
||||
Every search is one shot, and then that prisoner is off the list for a day.
|
||||
|
||||
## Priority: the search reads the tells
|
||||
|
||||
`GetPriority` is where suspicion becomes routing. Everyone is worth a routine toss; the tells push a
|
||||
prisoner up the queue:
|
||||
|
||||
```
|
||||
priority = 4 // baseline — everyone gets frisked
|
||||
+ 12 × bedDamageFraction // chewed furniture = probable cause
|
||||
+ min(8, timesCaught×2 + contrabandMade×0.5 + escapeAttempts×3) // the record
|
||||
```
|
||||
|
||||
| Term | Weight | Meaning |
|
||||
|---|---|---|
|
||||
| Base | 4 | a routine toss for every prisoner |
|
||||
| Gnawed bed | up to +12 | `1 − HP/maxHP` of their owned bed — a badly damaged bed shoots to the top |
|
||||
| `timesCaught` | ×2 | already caught with contraband before |
|
||||
| `contrabandMade` | ×0.5 | has finished contraband in the past |
|
||||
| `escapeAttempts` | ×3 | **the heaviest term** — a known tunneller is checked first, every time |
|
||||
| (record cap) | +8 max | the record cannot completely swamp fresh probable cause |
|
||||
|
||||
The record is read with `PeekFor` (a read-only lookup that never creates a record). The base of 4 for
|
||||
everyone is not an accident — **innocent prisoners still get frisked**, because the search must be
|
||||
able to come up empty or its mere offer would be the discovery.
|
||||
|
||||
**Worked example.** A prisoner whose bed sits at 40% HP (`missing = 0.6`), with 2 prior catches and
|
||||
1 foiled tunnel:
|
||||
|
||||
```
|
||||
priority = 4 + 12×0.6 + min(8, 2×2 + 0 + 1×3)
|
||||
= 4 + 7.2 + min(8, 7)
|
||||
= 4 + 7.2 + 7 = 18.2
|
||||
```
|
||||
|
||||
versus **4** for a clean prisoner with an intact bed. The warden walks past the quiet one and goes
|
||||
straight for the one who has been busy. This closes the loop [Improvised Weapons](Improvised-Weapons.md)
|
||||
opens: whittling a shiv chews the bed, the damage is probable cause, and the warden's *suspicion* —
|
||||
not the player's eye — sends them to search.
|
||||
|
||||
## The act: what a search does
|
||||
|
||||
`JobDriver_SearchPawn` walks the searcher to the subject, runs a **900-tick** wait toil with a
|
||||
progress bar, then resolves. Resolution always does two things, hit or miss:
|
||||
|
||||
1. `MarkSearched(subject)` — starts the daily cooldown.
|
||||
2. `timesSearched++` on the record — *"classification and the guard's future suspicion read how often
|
||||
this pawn has been turned over."*
|
||||
|
||||
Then it gathers everything the subject is hiding that this search can **reach** (`Reach` defaults to
|
||||
`OnBody | InCell`; a subclass can narrow it). If nothing is reachable, the job ends here — the time
|
||||
was still spent, and the player is told nothing.
|
||||
|
||||
### Find math, per hidden item
|
||||
|
||||
For each reachable secret:
|
||||
|
||||
```
|
||||
conceal = item.concealability // 0 = always found, 1 = never
|
||||
chance = clamp( (0.35 + 0.03 × socialSkill) × (1 − conceal) × 2 , 0.02 , 0.95 )
|
||||
|
||||
if item is a tunnel in progress:
|
||||
chance = clamp( chance + tunnelExtra , 0.02 , 0.98 ) // tunnelExtra up to +0.40
|
||||
|
||||
chance ×= WardenDisposition.Diligence(searcher) // the guard's thoroughness
|
||||
|
||||
if Rand.Chance( WardenDisposition.Corruption(searcher) × 0.6 ):
|
||||
continue // the warden saw it and said nothing — looked away
|
||||
|
||||
found = Rand.Chance(chance)
|
||||
```
|
||||
|
||||
Reading it in plain terms:
|
||||
|
||||
- **Skill raises the ceiling; concealability lowers it.** `BaseFindChance` is 0.35; each level of the
|
||||
warden's **Social** skill adds 0.03. The `(1 − conceal) × 2` factor pivots at concealability 0.5
|
||||
(neutral, ×1.0): a drug at 0.5 is average, a shiv at 0.75 is halved (×0.5), a heavy weapon at 0.35
|
||||
is boosted (×1.3).
|
||||
- **A good warden still misses a well-hidden shiv more often than not.** Worked: a level-8 Social
|
||||
warden searching for a 0.75-concealability shiv gets `(0.35 + 0.24) × 0.5 = 0.295` *before*
|
||||
Diligence scales it down further. Well under a coin-flip.
|
||||
- **A tunnel is a different story.** The pick hides at 0.5, but a nearly-finished shaft adds up to
|
||||
+0.40 and the cap rises to 0.98 — the deeper the dig, the harder it is to miss. A routine cell toss
|
||||
catches a dig in progress.
|
||||
- **The warden's own thoroughness and price** enter last: `Diligence` multiplies the whole chance
|
||||
down for a lax, kind, or miserable guard, and `Corruption` gives a bent guard a flat chance to look
|
||||
the other way even on an item they would otherwise have found. Both are on the
|
||||
[Corruption](Corruption.md) page.
|
||||
|
||||
### On a find
|
||||
|
||||
```
|
||||
Confiscate(subject, item.def) // it never becomes a Thing — nothing drops on the floor
|
||||
timesCaught++ // raises future suspicion, feeds classification
|
||||
if it was a tunnel: escapeAttempts++ // a foiled dig is a logged attempt, same as one that ran
|
||||
```
|
||||
|
||||
and the player gets a message — *"{warden} searched {prisoner} and found hidden {item}"*, or the
|
||||
tunnel variant. If nothing is found the player is told **nothing**: they do not learn there *was*
|
||||
something, only that the warden's time was spent.
|
||||
|
||||
## Writing back to the record
|
||||
|
||||
Every outcome updates Core's `CriminalRecord`, which is what makes the search a *loop* rather than a
|
||||
one-off dice roll:
|
||||
|
||||
| Field | Written when | Read by |
|
||||
|---|---|---|
|
||||
| `timesSearched` | every search, hit or miss | priority, suspicion, classification |
|
||||
| `timesCaught` | a find | priority (×2), classification |
|
||||
| `escapeAttempts` | a foiled tunnel | priority (×3, heaviest), classification |
|
||||
|
||||
If you run **Institution: Justice**, those same fields drive a prisoner's security classification and
|
||||
feed the deterrence signal — a prison that catches contraband becomes a prison that classifies its
|
||||
troublemakers correctly and deters the next attempt. If you run Contraband alone, the fields still
|
||||
route the warden. See [Compatibility](Compatibility.md).
|
||||
|
||||
## Quick reference
|
||||
|
||||
| Constant | Value | Meaning |
|
||||
|---|---|---|
|
||||
| Base find chance | 0.35 | before skill and concealability |
|
||||
| Per Social level | +0.03 | skill raises the ceiling |
|
||||
| Find chance clamp | 0.02 .. 0.95 | (0.98 for a tunnel) |
|
||||
| Search duration | 900 ticks | one wait toil, hit or miss |
|
||||
| Search cooldown | 60000 ticks (1 day) | per prisoner |
|
||||
| Priority base | 4 | every prisoner is worth a routine toss |
|
||||
| Gnawed-bed weight | up to +12 | `1 − HP/maxHP` |
|
||||
| Record weight | up to +8 | `timesCaught×2 + contrabandMade×0.5 + escapeAttempts×3` |
|
||||
| Default `Reach` | `OnBody \| InCell` | narrowed by a future street-frisk subclass |
|
||||
|
||||
---
|
||||
|
||||
*Part of the **Institution** suite — Core · Contraband · Justice · Gangs, alongside Foul Play and
|
||||
Ward. Each stands alone; together they interlock.*
|
||||
*Developed with substantial assistance from Claude (Anthropic).*
|
||||
Reference in New Issue
Block a user