Wiki: player-facing rewrite of every layer + add a scenario FAQ, linked from Home

This commit is contained in:
flan
2026-07-16 02:18:25 +00:00
parent ff703271ba
commit 8250dcb369
32 changed files with 1654 additions and 2510 deletions
+45 -110
View File
@@ -1,29 +1,18 @@
# 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.
It keys off the *kind of hold* a pawn is under. That one step of 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 feature hard-coding a single 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`.
A pawn's secured context boils down to one of three kinds and the question "who, if anyone, may search
them." Everything downstream reads that, so a new feature works across every kind of hold the day it's
written, and a new *kind of hold* works across every existing feature the day it's added.
---
## 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 |
@@ -32,121 +21,67 @@ public enum SecuredKind
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.
- **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 isn't innocence — it's only
the absence of an authority allowed to check.
- **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 warden for one and the overseer for the other. Features that only care "is someone entitled to
search this pawn" read whether the pawn is *held* and never branch on which.
## The struct
## Two questions, two axes
`SecuredContext` is a small `readonly struct` — the kind, the pawn, and two computed predicates.
The context answers two different questions, and picking the right one is the whole skill of using it:
```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… |
| Question | 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 |
| **Is this pawn held?** | Prisoner, Slave | **custody** — who is under the colony's control, who can be disciplined, whose cell can be searched |
| **Could this pawn be hiding something?** | 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.
For example, [Propensity](Propensity.md)'s Nurture uses *held* in its "held & unhappy compounds" lever
— 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, cares who *could* be hiding something to
decide who's even worth checking, and then checks standing to decide whether it's *allowed* to.
---
## `Of` — one pawn to a context
## How a pawn gets classified
```csharp
public static SecuredContext? Of(Pawn p)
```
Any pawn resolves to one context, or to **nothing at all** if the suite doesn't model them:
`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:
- Animals, mechs, and the dead are simply not the suite's business — they resolve to nothing, and
every sweep and check skips them without a special case.
- A pawn is tested as **prisoner** first, then **slave**, then **free colonist**. So a pawn who is
somehow both a colonist and imprisoned is classified by their *hold* first.
```
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.
Keeping "nothing at all" separate from "a free pawn we track" is deliberate: there's a real difference
between "a free pawn we watch" and "a pawn we don't model at all," and collapsing them would let
non-colony pawns leak into colony machinery. A single sweep can walk every prisoner, slave, and
colonist on a map in one pass, skipping everything that resolves to nothing.
---
## 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:
This is the design decision the whole page exists to justify. If every feature checked "is this pawn a
prisoner" 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.
- adding **slaves** to a feature would mean revisiting every feature;
- adding a **ward patient** mode would mean revisiting every feature;
- adding a future **secure-zone** concept would mean revisiting every feature.
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`.
By routing all of them through one question — *what kind of hold is this?* — a new kind of hold is
added *once*, and every existing feature inherits it. The concealment, search, and needs machinery
never has to learn what a slave is; it only ever asked whether the pawn is *held* and whether they
*could conceal*.
### 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.
There's no separate "ward patient" kind, and its absence is intentional. A committed ward patient
**is** a prisoner in the game's own sense — Institution: Ward builds its committed-patient mode on top
of vanilla's prisoner system — so such a pawn already counts as **Prisoner** here, with no special case
and no dependency from Core (or Contraband) onto Ward. Every feature treats a ward patient exactly as
it treats any prisoner. The right amount of new work to support Ward's hold semantics was zero, and
that's the payoff of keying off the kind of hold instead of the label.
---