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
+55 -120
View File
@@ -1,61 +1,40 @@
# The Treatment Engine — shared rehabilitation
*`TreatmentProgram` + `TreatableConditionExtension`, in `Source/Core/TreatmentProgram.cs`.*
*The shared maths behind every "fix a held pawn over time" system in the suite.*
Core carries one more thing every "fix a held pawn over time" system needs and none should own alone:
the maths of a **treatment programme**. Sustained attention in a secured facility reduces a marked
condition, the room's quality helps or hurts, and once there is nothing left to reduce a **recovery
track** builds toward a dischargeable state.
Core carries one more thing every rehabilitation-style system needs and none should own alone: the
maths of a **treatment programme**. Sustained attention in a secured facility reduces a marked
condition, the room's quality helps or hurts, and once there's nothing left to reduce a **recovery
track** builds toward a state where the pawn can be discharged.
It exists for the same reason the propensity engine does. Two systems want the identical loop pointed
at different states — **Ward** treats the mental illness that got a pawn committed; **Justice** wants
to rehabilitate the disposition that got one imprisoned — and without a shared engine each would grow
its own copy and drift. Core owns **only the maths**. Which interaction mode enrols a pawn, which
hediffs and thoughts carry the flavour, which jobs run the sessions, and which alert fires on
discharge all stay in the consuming mod.
at different things — **Ward** treats the mental illness that got a pawn committed; **Justice**
rehabilitates the disposition that got one imprisoned — and without a shared engine each would grow its
own copy and drift apart. Core owns **only the maths**. Which interaction enrols a pawn, which
conditions and moods carry the flavour, which jobs run the sessions, and which alert fires on discharge
all live in the mod using it.
> The engine does something concrete when there is a real condition to work through, and is a
> **no-op** otherwise. A colony that marks nothing treatable never notices it exists.
> The engine does something only when there's a real condition to work through, and does nothing
> otherwise. A colony that never marks anything treatable never notices it exists.
---
## The two pieces
## How treatment plays out
### `TreatableConditionExtension` — marking a condition
A condition has to be **marked treatable** before a programme can touch it (Ward marks mental illness
this way; a modder or player can mark anything — see the [Modder API](Modder-API.md) page). Once a
condition is marked, treatment runs in sessions:
A `DefModExtension` you attach to a `HediffDef` to declare it something a programme can reduce.
```xml
<HediffDef Name="SomeDisorder">
<modExtensions>
<li Class="Contraband.TreatableConditionExtension">
<reductionPerSession>0.12</reductionPerSession>
</li>
</modExtensions>
</HediffDef>
```
| Field | Default | Meaning |
|---|---:|---|
| `reductionPerSession` | `0.12` | Severity removed per **completed** session, *before* the caller's skill / facility / share scale it. |
A full course is several sessions by design — the programme has to be **sustained**, not run once. The
extension carries no flavour; it says only *how fast this condition yields*. Ward attaches it to
mental illness (via a soft, conditional patch on Rim Disorders' depression, anxiety, PTSD, OCD);
anything a mod or a player marks is treated identically, so opting a hediff in is a one-line patch.
### `TreatmentProgram` — the maths
A static, null-safe class. It holds no state of its own — it reads and mutates hediffs on the pawn.
| Call | Returns | What it does |
|---|---|---|
| `TreatableConditions(pawn)` | `List<Hediff>` | every marked hediff on the pawn that still has severity |
| `HasTreatableCondition(pawn)` | `bool` | is there any condition left to work through? |
| `ReduceConditions(pawn, strength)` | `void` | lower every marked condition by `reductionPerSession × strength`; remove any that reach ~0 |
| `FacilityQuality(pawn)` | `float 0.5..1.5` | how much the pawn's room helps or hurts |
| `AdvanceRecovery(pawn, recoveryDef, amount)` | `float` | advance (creating if absent) a recovery hediff, clamped to `1.0`; returns the new severity |
| `RecoveryLevel(pawn, recoveryDef)` | `float 0..1` | current level of that recovery track, or `0` |
1. **Each completed session removes a slice of the condition's severity.** The base amount is **0.12
severity per session**, *before* it's scaled by the treating pawn's skill, the facility's quality,
and how much of the session that patient actually gets. When a condition's severity reaches zero
it's gone.
2. **A full course is several sessions by design.** The programme has to be *sustained* — you can't
fix a pawn in a single visit.
3. **Once nothing is left to treat, continued care builds a recovery track** from 0 toward 1
(discharge-ready). Fix the illness first, then stabilise.
4. **The recovery track decays on its own if you stop.** Recovery you stop maintaining slips back down
— which is what makes the whole thing a loop you keep up, not a one-time unlock.
---
@@ -63,96 +42,52 @@ A static, null-safe class. It holds no state of its own — it reads and mutates
### Facility quality
```
room == null OR psychologically outdoors → 0.75 (a poor makeshift facility, not zero)
otherwise → Clamp(0.6 + impressiveness / 120, 0.5, 1.5)
```
The room the treatment happens in scales every session between **0.5×** (grim) and **1.5×**
(excellent):
| Room impressiveness | Factor |
| Room | Factor |
|---|---:|
| 0 (bare) | `0.60` |
| ~48 (decent) | `1.00` |
| ≥108 (impressive) | `1.50` (capped) |
| outdoors / none | `0.75` |
| Bare (0 impressiveness) | 0.60 |
| Decent (~48 impressiveness) | 1.00 |
| Impressive (108+ impressiveness) | 1.50 (capped) |
| Outdoors / no room | 0.75 |
The clamp is deliberate: a grim, filthy, cramped facility heals worse, a calm clean one better, but a
palace can't trivialise the labour the programme costs (1.5× ceiling), and even nowhere is a poor
makeshift room, not a hard zero.
The limits are deliberate: a grim, filthy, cramped facility heals worse and a calm clean one better,
but a palace can't trivialise the work the programme costs (1.5× ceiling), and even treating a pawn out
in the open is a poor makeshift room (0.75×), not a hard zero.
### Reducing a condition
```
h.Severity -= reductionPerSession × strength // per marked hediff
if h.Severity <= 0.001 → remove it
```
Each marked condition drops by **0.12 × the session's combined strength** per session, and vanishes
when it hits zero. That combined strength is the treating pawn's skill × the facility quality above ×
the patient's share of the session — so all three inputs matter, and a good therapist in a good room
working one-on-one is worth many times a poor one splitting attention in a bad room.
`strength` is the **caller's** combined factor — skill × facility quality × this pawn's share of the
session. The engine does not define it; the consumer does, so a one-on-one session at full skill in a
good room reduces far more than a distracted share in a squalid one.
### Building recovery
### Advancing recovery
```
recovery = pawn's hediff of recoveryDef (created at 0.001 if absent)
recovery.Severity = min(1.0, recovery.Severity + amount)
```
The recovery track is a plain `0..1` severity the consumer supplies the def for (Ward's
`Ward_Recovery`). Core only advances it; the consumer decides what its top stage *unlocks* (Ward: a
"ready for discharge" alert) and — crucially — gives the hediff a **negative `severityPerDay`** so it
**decays without sustained attention**. Recovery you stop maintaining slips back; that decay is what
makes the programme a loop rather than a one-time unlock.
The recovery track is a plain 0-to-1 progress bar toward discharge. Continued care adds to it (capped
at 1.0) and — crucially — it carries a slow decay, so it slips back down without sustained attention.
The mod using the engine decides what reaching the top actually *unlocks* (Ward: a "ready for
discharge" alert).
---
## The consumer contract
Core draws a hard line at "only the maths". A consumer owns everything with flavour:
| Core owns (the maths) | The consumer owns (the flavour) |
|---|---|
| `TreatableConditions` / `HasTreatableCondition` | which hediffs are marked treatable |
| `ReduceConditions` | the job/interaction that runs a session |
| `FacilityQuality` | the room role that makes a "facility" |
| `AdvanceRecovery` / `RecoveryLevel` | the recovery hediff def + what its stable stage unlocks |
| — | the discharge alert, the thoughts, the decay rate |
The idiomatic session, drawn from Ward's `JobDriver_PsychiatricCare` (the reference consumer):
```csharp
using Contraband;
float skill = 0.7f * social + 0.3f * medicine; // the consumer's own blend
float quality = TreatmentProgram.FacilityQuality(patient);
float strength = (0.5f + skill / 20f * 0.5f) * quality * share; // 1.0 share for the primary patient
// 1. Reduce the actual condition first.
TreatmentProgram.ReduceConditions(patient, strength);
// 2. Only once nothing is left to treat does recovery advance -- fix the illness, then stabilise.
if (!TreatmentProgram.HasTreatableCondition(patient))
{
TreatmentProgram.AdvanceRecovery(patient, MyDefOf.RecoveryHediff, 0.15f * quality * share);
}
```
"Reduce the condition, *then* build recovery" is the shape Ward uses, but it is a convention, not a
rule Core enforces — a rehabilitation consumer with no clinical condition to clear (Justice's reform)
can advance a recovery track from the first session and read `RecoveryLevel` to gate a parole.
---
## The two consumers
## Who uses it
- **[Institution: Ward](https://git.onetick.ninja/flan/rimworld-ward)** — psychiatric care. Marks
mental-illness hediffs treatable, runs warden counselling sessions, builds `Ward_Recovery` toward a
"ready for discharge" alert. The reference implementation.
- **Institution: Justice** — rehabilitation. Its `reform` score is the disposition axis punishment
mental-illness conditions treatable (Rim Disorders' depression, anxiety, PTSD, and OCD, when that mod
is present), runs warden counselling sessions, and builds a recovery track toward a "ready for
discharge" alert. The reference example.
- **Institution: Justice** — rehabilitation. Its *reform* score is the disposition axis punishment
moves; the recovery track is the natural home for a *sustained rehabilitation programme* that gates
parole on the same shared engine, so the two systems agree on "getting better" instead of each
inventing it.
The point, exactly as with propensity: one engine, two states, no drift.
"Reduce the condition, *then* build recovery" is the shape Ward uses, but it isn't forced: a
rehabilitation system with no clinical condition to clear (like Justice's reform) can build a recovery
track from the very first session and read its level to gate a parole.
The point, exactly as with propensity: one engine, two uses, no drift.
---