Wiki: player-facing rewrite of every layer + add a scenario FAQ, linked from Home
This commit is contained in:
@@ -4,177 +4,161 @@
|
||||
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.**
|
||||
Searching a prisoner is not one of them, and no such job exists anywhere in the base game. Contraband
|
||||
adds it as a **new** warden job, not a change to an existing 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:
|
||||
The design splits one job into two halves:
|
||||
|
||||
| Class | Question it answers | Notes |
|
||||
| Half | 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 search **assignment** | **Who** may be searched? | it is ordinary warden work — a warden already has the run of the prison, so no new authority is invented |
|
||||
| The search **act** | **What** is a search? | walk to the pawn, turn them over, resolve |
|
||||
|
||||
> The WorkGiver is the authority. This driver is just the act.
|
||||
> The assignment is the authority. The act 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.
|
||||
Police** module adds a *second* authority — a cop who may stop and frisk a **free colonist** — reusing
|
||||
the same act with a **narrower reach** (a street frisk gets what is on the body, 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
|
||||
## Who gets searched
|
||||
|
||||
`Contraband_WardenSearch` is a `Warden` work-type giver:
|
||||
The search is warden work, and it sits at a deliberate spot in the warden's priorities:
|
||||
|
||||
| Field | Value | Why |
|
||||
| Property | 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 |
|
||||
| Work type | Warden | it is warden work |
|
||||
| Priority within warden work | **below feeding** | a starving prisoner matters more than a hidden shiv |
|
||||
| Capacities needed | Manipulation, Sight | you search with your hands and eyes |
|
||||
| Ranked by suspicion | **yes** | pick the right prisoner, not the nearest |
|
||||
|
||||
`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.
|
||||
That last one — ranking by suspicion rather than distance — is what lets a gnawed bed and a thick
|
||||
record send the warden to the *right* prisoner first.
|
||||
|
||||
`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.
|
||||
A search is offered only if all of these hold: the warden should be looking after this prisoner, the
|
||||
per-prisoner cooldown is up, the prisoner is **awake, not downed, and not in a mental state**, and the
|
||||
warden can reach them.
|
||||
|
||||
Note what is **not** checked — and the source is emphatic about it:
|
||||
Note what is **not** checked:
|
||||
|
||||
> 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
|
||||
> 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.
|
||||
The same prisoner cannot be turned over more than **once per day** (60,000 ticks). 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:
|
||||
Everyone is worth a routine toss; the tells push a prisoner up the queue. Priority is built up like
|
||||
this:
|
||||
|
||||
```
|
||||
priority = 4 // baseline — everyone gets frisked
|
||||
+ 12 × bedDamageFraction // chewed furniture = probable cause
|
||||
+ min(8, timesCaught×2 + contrabandMade×0.5 + escapeAttempts×3) // the record
|
||||
```
|
||||
- **Base 4** — a routine toss for every prisoner.
|
||||
- **Up to +12 for a gnawed bed** — scaled by how damaged their owned bed is. A badly damaged bed
|
||||
shoots to the top.
|
||||
- **Up to +8 for their record** — prior catches (×2 each), contraband made in the past (×0.5 each),
|
||||
and foiled escape attempts (×3 each, **the heaviest term**), capped at +8 so the record cannot
|
||||
completely swamp fresh probable cause.
|
||||
|
||||
| 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 |
|
||||
| Gnawed bed | up to +12 | how far below full HP their owned bed sits |
|
||||
| Times caught | ×2 | already caught with contraband before |
|
||||
| Contraband made | ×0.5 | has finished contraband in the past |
|
||||
| Escape attempts | ×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.
|
||||
The record is only read here, never created. 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:
|
||||
**Worked example.** A prisoner whose bed sits at 40% HP (so 60% missing), 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
|
||||
```
|
||||
> 4 + (12 × 0.6) + min(8, 2×2 + 1×3) = 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.
|
||||
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:
|
||||
The searcher walks to the subject, spends a **900-tick** turn 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."*
|
||||
1. Starts the daily cooldown on that prisoner.
|
||||
2. Ticks up how many times this pawn has been searched — *"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.
|
||||
Then it goes through everything the subject is hiding that this search can **reach** (a full warden
|
||||
search reaches both the body and the cell; a narrower future street-frisk reaches only the body). 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:
|
||||
For each reachable secret, the chance to find it is built like this:
|
||||
|
||||
```
|
||||
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)
|
||||
```
|
||||
- Start from a **base of 0.35**, plus **0.03 for each level of the warden's Social skill**.
|
||||
- Scale by how concealable the item is: the factor pivots at concealability 0.5. A drug at 0.5 is
|
||||
average (×1.0), a shiv at 0.75 is halved (×0.5), a heavy weapon at 0.35 is boosted (×1.3).
|
||||
- If the item is a **tunnel in progress**, add its extra find chance (up to +0.40 for a nearly
|
||||
finished shaft), and the ceiling rises.
|
||||
- Multiply the whole thing by the warden's **Diligence** — a lax, kind, or miserable guard scales it
|
||||
down.
|
||||
- Before the find roll, a **bent** warden gets a flat chance to look the other way (their Corruption ×
|
||||
0.6) even on an item they would otherwise have found.
|
||||
- The chance is clamped to between 0.02 and 0.95 (0.98 for a tunnel).
|
||||
|
||||
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.
|
||||
- **Skill raises the ceiling; concealability lowers it.** Each level of the warden's Social skill adds
|
||||
a little; a well-hidden item takes a lot of it back.
|
||||
- **A good warden still misses a well-hidden shiv more often than not.** A level-8 Social warden
|
||||
searching for a 0.75-concealability shiv is already well under a coin-flip *before* Diligence scales
|
||||
it down further.
|
||||
- **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
|
||||
- **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
|
||||
```
|
||||
- The item is confiscated — it **never becomes a real object**, nothing drops on the floor.
|
||||
- The prisoner's times-caught tally ticks up (raising future suspicion, feeding classification).
|
||||
- If it was a tunnel, their escape-attempt tally ticks up too — a foiled dig counts the 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.
|
||||
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
|
||||
Every outcome updates Core's criminal record, which is what makes the search a *loop* rather than a
|
||||
one-off dice roll:
|
||||
|
||||
| Field | Written when | Read by |
|
||||
| Tally | Updated 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 |
|
||||
| Times searched | every search, hit or miss | priority, suspicion, classification |
|
||||
| Times caught | a find | priority (×2), classification |
|
||||
| Escape attempts | a foiled tunnel | priority (×3, heaviest), classification |
|
||||
|
||||
If you run **Institution: Justice**, those same fields drive a prisoner's security classification and
|
||||
If you run **Institution: Justice**, those same tallies 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
|
||||
troublemakers correctly and deters the next attempt. If you run Contraband alone, the tallies still
|
||||
route the warden. See [Compatibility](Compatibility.md).
|
||||
|
||||
## Quick reference
|
||||
@@ -184,15 +168,14 @@ route the warden. See [Compatibility](Compatibility.md).
|
||||
| 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 |
|
||||
| Search duration | 900 ticks | one turn, hit or miss |
|
||||
| Search cooldown | 60,000 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 |
|
||||
| Gnawed-bed weight | up to +12 | how far below full HP their bed sits |
|
||||
| Record weight | up to +8 | caught ×2 + made ×0.5 + escapes ×3 |
|
||||
| Default reach | body + cell | narrowed by a future street-frisk |
|
||||
|
||||
---
|
||||
|
||||
*Part of the **Institution** suite — Core · Contraband · Justice · Gangs, alongside Foul Play and
|
||||
Ward. Each stands alone; together they interlock.*
|
||||
**
|
||||
|
||||
Reference in New Issue
Block a user