Skip to content

🗺️ Health — Mental Model

This page is a map, not API reference.

It answers one question:

Where does my code plug in, and what layer does what?

If you understand this page, you’ll know: - where damage/heal logic belongs - what is allowed to mutate state (and what must not) - how to extend Health without touching HealthSystem


🧩 The Big Idea

Health is a pipeline:

  • Inputs enter as data (DamageContext, HealContext)
  • Rules shape that data (PRE) and react to outcomes (POST)
  • Shields intercept damage before HP changes
  • HealthSystem is the only owner of HP state and mutation
  • Handlers add optional gameplay behaviour around that core
  • UI / Debug reads interfaces and visualises state

🔄 Mental Model Diagram

Gameplay intent
   │
   ├─► Build context (DamageContext / HealContext)
   │
   ├─► PRE rules (mutate context)
   │       - can change numbers
   │       - can cancel the attempt
   │
   ├─► Shields (IShield / ShieldChain)
   │       - can absorb damage before HP changes
   │
   ├─► HealthSystem (mutates HP)
   │       - clamps
   │       - raises events
   │       - runs death pipeline
   │
   └─► POST observers
           - react to FinalApplied (VFX/SFX/telemetry/reflect/lifesteal)
           - must not change the outcome

Healing is the same idea, minus shields:

Heal intent
   │
   ├─► Build HealContext
   ├─► PRE heal rules (mutate context)
   ├─► Healing modifiers (IHealingModifier)
   ├─► HealthSystem.TryHeal (mutates HP)
   └─► POST heal observers (SFX/VFX/events/telemetry)

✅ What Mutates vs What Observes

Layer Can mutate HP? Can change outcome? Should contain gameplay logic?
Shared types (DamageContext, HealContext)
PRE rules (IDamageRule, IHealRule) ✔ (by mutating/cancelling) ✔ (math + gates only)
Shields (IShield) ✔ (absorb some/all damage) ✔ (protection behaviour)
HealthSystem ✖ (no game-specific decisions)
POST observers (IPostDamageRule, IPostHealRule) ✔ (reactions only)
Handlers ✔ (via public APIs) ✔ (via public APIs) ✔ (gameplay behaviour)
UI / Debug ✖ (presentation/testing only)

🧠 Where To Put Things

If you want to…

  • Prevent damage from applying → PRE damage rule (IDamageRule) or Authority gate
  • Apply armor / resist / crit math → PRE damage rule
  • Absorb damage with a resource pool → Shield (IShield) or ShieldChain
  • React to damage (VFX, SFX, telemetry) → POST observer (IPostDamageRule)
  • Heal boosts / anti-heal → PRE heal rule (IHealRule)
  • Passive “healing received” scalingIHealingModifier
  • Regenerate over time → Handler (HealthRegenerationHandler)
  • Temporary invulnerability windows → Handler (HealthInvincibilityHandler)
  • Extra life / last standIBeforeDeathHandler (lifecycle seam)
  • Animator death hooksIHealthDeathHandler or a handler that subscribes to events
  • UI bars / indicators → UI connectors reading IHealthReadonly

🧷 Extension Points (Supported Seams)

Damage

  • PRE: IDamageRule (mutate/cancel)
  • POST: IPostDamageRule (observe)
  • Affinities: IDamageAffinity (victim-side multipliers)
  • Attacker scaling: IAttackerDamageModifier
  • Shields: IShield, optional IShieldPreview

Healing

  • PRE: IHealRule
  • POST: IPostHealRule
  • Modifiers: IHealingModifier

Lifecycle

  • Before death: IBeforeDeathHandler (can cancel death)
  • Death pipeline: IHealthDeathHandler (side effects only)
  • Authority: IHealthAuthority (mutation permission)

⚠️ Common Mistakes

  • Putting regen in rules
    Regen is time-based behaviour → use a handler.

  • Mutating health inside rules/observers
    Rules should mutate contexts, not HP. Use public HealthSystem APIs if you must act.

  • Using ShieldPool as a real shield
    ShieldPool is a ticketed accumulator and does not absorb damage.

  • Relying on preview as “truth”
    Previews are best-effort. Shields without IShieldPreview will be ignored in preview calculations.

  • Assuming multiplayer replication
    Authority gates mutation only. Replication is your netcode’s job.


🔗 Next Steps