# 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 `` 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).*