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,153 @@
|
||||
# Secured Context — the kind of hold a pawn is under
|
||||
|
||||
The suite has a rule that looks small and is load-bearing: **nothing keys off "is this a prisoner."**
|
||||
It keys off `SecuredContext` — the *kind of hold* a pawn is under. That indirection is why a ward
|
||||
patient, a slave, and (later) anyone inside a secure zone get the same concealment, search, schedule,
|
||||
and needs machinery for free, without any module hard-coding a pawn status.
|
||||
|
||||
`SecuredContext` reduces a pawn to one of three kinds and the question "who, if anyone, may search
|
||||
them." Everything downstream reads that, so a new module works across every context the day it is
|
||||
written, and a new *context* works across every existing module the day it is added.
|
||||
|
||||
Verified against `Source/Core/SecuredContext.cs`.
|
||||
|
||||
---
|
||||
|
||||
## The three kinds
|
||||
|
||||
```csharp
|
||||
public enum SecuredKind
|
||||
{
|
||||
Free, // a free colonist
|
||||
Prisoner, // a prisoner of the colony
|
||||
Slave, // a slave (Ideology)
|
||||
}
|
||||
```
|
||||
|
||||
| Kind | Who it is | May search them | Can conceal? | Held against their will? |
|
||||
|---|---|---|---|---|
|
||||
| **Free** | a free colonist | *no one* — until a secure-area or policing layer grants standing | **yes** — they can hoard | no |
|
||||
| **Prisoner** | a prisoner of the colony | the **warden** | yes | **yes** |
|
||||
| **Slave** | a slave (Ideology DLC) | the **overseer** | yes | **yes** |
|
||||
|
||||
Two subtleties fall out of this table:
|
||||
|
||||
- **Free colonists can still conceal.** A free colonist "can hoard contraband, but no one has
|
||||
standing to search them until a secure-area or policing layer grants it." Freedom is not innocence —
|
||||
it is only the absence of an authority permitted to check. That is why `CanConceal` (below) includes
|
||||
the free.
|
||||
- **Prisoner and Slave differ only in who holds the keys.** Both are "held"; the search authority is
|
||||
the warden for one and the overseer for the other. Downstream modules that only care "is someone
|
||||
entitled to search this pawn" read `IsHeld` and never branch on which.
|
||||
|
||||
## The struct
|
||||
|
||||
`SecuredContext` is a small `readonly struct` — the kind, the pawn, and two computed predicates.
|
||||
|
||||
```csharp
|
||||
public readonly struct SecuredContext
|
||||
{
|
||||
public readonly SecuredKind Kind;
|
||||
public readonly Pawn Pawn;
|
||||
|
||||
// Held against their will -- prisoner or slave. Excludes a free colonist.
|
||||
public bool IsHeld => Kind == SecuredKind.Prisoner || Kind == SecuredKind.Slave;
|
||||
|
||||
// Anyone with something to hide -- includes free colonists (who may hoard).
|
||||
public bool CanConceal => Pawn != null;
|
||||
}
|
||||
```
|
||||
|
||||
The two predicates split the world along two different axes, and picking the right one is the whole
|
||||
skill of using this type:
|
||||
|
||||
| Predicate | True for | Ask it when you care about… |
|
||||
|---|---|---|
|
||||
| `IsHeld` | Prisoner, Slave | **custody** — who is under the colony's control, who can be disciplined, whose cell can be searched |
|
||||
| `CanConceal` | Free, Prisoner, Slave (any real pawn) | **contraband** — who could be *hiding* something, regardless of status |
|
||||
|
||||
For example, [Propensity](Propensity.md)'s Nurture uses `IsHeld` in its "held & unhappy compounds"
|
||||
clause — it only wants to pile the ×1.4 penalty on a pawn the colony actually holds and mistreats, not
|
||||
on a grumpy free colonist. A warden-search feature, by contrast, would gate on `CanConceal` to decide
|
||||
who is even worth checking, and then on standing to decide whether it is *allowed* to.
|
||||
|
||||
---
|
||||
|
||||
## `Of` — one pawn to a context
|
||||
|
||||
```csharp
|
||||
public static SecuredContext? Of(Pawn p)
|
||||
```
|
||||
|
||||
`Of` maps a pawn to a context, or **`null`** if the pawn has none. The order of the checks matters and
|
||||
is worth reading literally:
|
||||
|
||||
```
|
||||
if p is not humanlike, or p is dead -> null (not one of ours)
|
||||
if p.IsPrisonerOfColony -> Prisoner
|
||||
if p.IsSlaveOfColony -> Slave
|
||||
if p.IsFreeColonist -> Free
|
||||
otherwise -> null
|
||||
```
|
||||
|
||||
- **The guard comes first.** Animals, mechs, and the dead are simply not the suite's business — they
|
||||
return `null`, and every `OnMap` loop and downstream check skips them without a special case.
|
||||
- **Prisoner and Slave are tested before Free.** A pawn who is somehow both a colonist and imprisoned
|
||||
is classified by their *hold* first. The nullable return is the clean signal for "this pawn is
|
||||
outside the model," so callers pattern-match `HasValue` rather than defaulting to some "Free-ish"
|
||||
status.
|
||||
|
||||
Returning `SecuredContext?` (nullable) rather than a `Free` fallback is deliberate: there is a real
|
||||
difference between "a free pawn we track" and "a pawn we do not model at all," and collapsing them
|
||||
would let non-colony pawns leak into colony machinery.
|
||||
|
||||
## `OnMap` — every context on a map
|
||||
|
||||
```csharp
|
||||
public static IEnumerable<SecuredContext> OnMap(Map map)
|
||||
```
|
||||
|
||||
`OnMap` yields a context for every spawned pawn that has one — prisoners, slaves, and colonists,
|
||||
skipping everything `Of` returns `null` for. It is a lazy `yield` iterator, so a caller can enumerate
|
||||
"everyone the suite cares about on this map" in one loop:
|
||||
|
||||
```csharp
|
||||
foreach (SecuredContext ctx in SecuredContexts.OnMap(map))
|
||||
{
|
||||
if (ctx.IsHeld) { /* consider only the held */ }
|
||||
}
|
||||
```
|
||||
|
||||
A `null` map yields nothing (an empty sequence), so callers do not need to null-check before looping.
|
||||
|
||||
---
|
||||
|
||||
## Why not just "prisoner"?
|
||||
|
||||
This is the design decision the whole page exists to justify, so it is worth stating plainly. If every
|
||||
module checked `pawn.IsPrisonerOfColony` directly, then:
|
||||
|
||||
- adding **slaves** to a feature would mean editing every module;
|
||||
- adding a **ward patient** mode would mean editing every module;
|
||||
- adding a future **secure-zone** concept would mean editing every module.
|
||||
|
||||
By routing all of them through one predicate, a new hold-kind is added *once*, in Core, and every
|
||||
existing module inherits it. The concealment, search, and needs machinery never has to learn what a
|
||||
slave is — it only ever asked `IsHeld` and `CanConceal`.
|
||||
|
||||
### The deliberately-absent Ward kind
|
||||
|
||||
There is no `SecuredKind.WardPatient`, and its absence is intentional. From the source:
|
||||
|
||||
> *"a committed patient IS a prisoner in vanilla's sense (Ward rides the prisoner rail), so it is
|
||||
> `SecuredKind.Prisoner` here and needs no special case. Contraband does not depend on Ward."*
|
||||
|
||||
Because Institution: Ward implements a committed patient *on top of* vanilla's prisoner rail, such a
|
||||
pawn already returns `Prisoner` from `Of` — no new kind, no dependency from Core (or Contraband) onto
|
||||
Ward, and every module treats a ward patient exactly as it treats any prisoner. The right amount of
|
||||
new code to support Ward's hold semantics was zero, and that is the payoff of keying off the context
|
||||
instead of the label.
|
||||
|
||||
---
|
||||
|
||||
*Part of the **Institution** suite. AI disclosure: developed with substantial assistance from Claude (Anthropic).*
|
||||
Reference in New Issue
Block a user