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.
9.5 KiB
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 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:
MarkSearched(subject)— starts the daily cooldown.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.
BaseFindChanceis 0.35; each level of the warden's Social skill adds 0.03. The(1 − conceal) × 2factor 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.295before 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:
Diligencemultiplies the whole chance down for a lax, kind, or miserable guard, andCorruptiongives 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 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.
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).