⚙️ Health — Rules Core¶
This folder contains the runtime backbone of the Health rule system.
It defines the data structures, hubs, and utilities that power both the damage and healing pipelines. Every rule, modifier, and observer ultimately flows through the types in this folder.
Nothing here encodes gameplay decisions — this layer exists purely to orchestrate, order, and safely execute rules.
📦 What Lives Here (and Why)¶
The Rules Core layer is responsible for: - Transporting damage and heal data through the pipeline - Providing safe, explicit mutation surfaces for PRE rules - Dispatching finalized results to POST observers - Keeping execution deterministic and low-allocation at runtime
It deliberately does not: - Apply health directly - Contain gameplay logic - Know about abilities, buffs, UI, or networking
🧱 Core Context Structs¶
DamageContext¶
Encapsulates a single damage attempt as it flows through:
PRE rules → Shields → HealthSystem → POST rules
It is passed by ref to PRE rules and by readonly ref to POST rules.
Field Categories¶
| Category | Fields | Purpose |
|---|---|---|
| Inputs | RawAmount, Tags |
Base damage and semantic flags (Melee, Fire, Poison, etc.). |
| Mutables (PRE) | Multiplier, FlatDelta, BypassShields, BypassRules, Cancelled |
Edited by PRE rules to shape the hit. |
| Outputs | FinalApplied |
Final HP lost after all rules, shields, and clamps. |
| Metadata | Attacker, Victim, TeamId, SourceId, HitPoint, HitNormal, Direction, Impulse |
Context for AI, VFX, analytics, and reactions. |
PRE rules may:
- mutate values
- set Cancelled = true
- return false to short-circuit the chain
POST rules may: - observe only - never mutate or cancel
Helper Construction¶
DamageContext provides factory helpers to reduce boilerplate and ensure
consistent defaults when creating damage attempts:
CreateBasic(attacker, victim, amount, tags)
Additional metadata (hit point, impulse, source id, team id, bypass flags, etc.) is applied by direct field assignment before the context enters the pipeline. This keeps construction explicit and avoids hidden allocations or side effects.
HealContext¶
Healing counterpart to DamageContext, flowing through:
PRE heal rules → modifiers → HealthSystem → POST heal rules
Key Fields¶
| Field | Purpose |
|---|---|
RawAmount |
Base heal before any rules. |
Multiplier / FlatDelta |
Modified by PRE heal rules. |
Cancelled |
Abort healing before application. |
FinalApplied |
Actual HP restored. |
Heal rules never mutate health directly — they only shape the final input
to HealthSystem.TryHeal.
🧠 Rule Hubs¶
DamageRuleHub¶
Central aggregator and executor for damage-side rules.
public sealed class DamageRuleHub : MonoBehaviour
{
bool ApplyRules(ref DamageContext ctx);
void NotifyPost(in DamageContext ctx);
}
Behaviour¶
- PRE phase
- Gathers all
IDamageRulecomponents - Sorts by ascending
Priority -
Stops execution if:
- a rule returns
false, or ctx.Cancelledis set
- a rule returns
-
POST phase
- Invokes all
IPostDamageRuleobservers - Notified once per attempt when the engine finalizes the outcome
- Receives the finalized
DamageContext(includingFinalApplied)
The hub refreshes its cache on:
- Awake
- OnEnable
This makes it safe for: - pooling - runtime enable/disable - prefab composition changes
HealRuleHub¶
Exact structural mirror of DamageRuleHub, but for healing.
- Executes
IHealRulein ascending priority - Short-circuits on
falseorctx.Cancelled - Invokes all
IPostHealRuleobservers after healing is applied - Ideal for:
- anti-heal
- clutch boosts
- SFX / VFX
- telemetry
🧠 Design Guarantees¶
- Rule hubs avoid per-hit allocations during execution
- Context structs are explicit and self-documenting
- PRE rules mutate context — POST rules observe results
- Components can be added/removed at runtime
- Each
HealthSysteminstance can define its own rule composition
This layer exists to stay boring, stable, and predictable.
❌ What This Layer Should Not Do¶
- Do not apply health directly
- Do not contain gameplay decisions
- Do not assume networking or authority
- Do not encode UI or presentation logic
If something here feels “game-specific”, it’s in the wrong place.