Files
institution/Wiki/core/Treatment-Engine.md
T
flan 3530100ed5 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.
2026-07-15 20:30:38 +00:00

7.0 KiB
Raw Blame History

The Treatment Engine — shared rehabilitation

TreatmentProgram + TreatableConditionExtension, in Source/Core/TreatmentProgram.cs.

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.

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.

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 two pieces

TreatableConditionExtension — marking a condition

A DefModExtension you attach to a HediffDef to declare it something a programme can reduce.

<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

The numbers

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)
Room impressiveness Factor
0 (bare) 0.60
~48 (decent) 1.00
≥108 (impressive) 1.50 (capped)
outdoors / none 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.

Reducing a condition

h.Severity -= reductionPerSession × strength      // per marked hediff
if h.Severity <= 0.001  →  remove it

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.

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 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):

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

  • Institution: 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 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.


Part of the Institution suite. AI disclosure: developed with substantial assistance from Claude (Anthropic).