Skip to content

⚙️ Health — Rules (Root)

This folder is the entry point for the Health rule system.

Rules define how damage and healing behave between incoming data (DamageContext / HealContext) and the core HealthSystem. They are the primary extension surface for combat math, modifiers, and reactive behaviour.


📦 What Rules Are (and Aren’t)

Rules are - Declarative behaviour modifiers - Ordered, deterministic pipeline steps - Fully modular and opt-in - Safe to add/remove per-prefab

Rules are not - Core health ownership - Networking or replication logic - UI or presentation systems

Rules sit between input and mutation — nothing more, nothing less.


🧩 What Lives in the Rules Root

Priority Definitions

  • DamageRulePriority.cs
  • HealRulePriority.cs

These files define the canonical ordering constants used by all rule families.

They live in the Rules root so: - Every rule can reference them - No circular dependencies are introduced - Ordering stays globally consistent

Note

Keep priority constants in the Rules root.
Centralising ordering is what keeps the pipeline deterministic and debuggable.


🔄 Pipeline Overview

Damage Pipeline

Incoming DamageContext
   └─► PRE Damage Rules (ascending Priority)
        0–49     Setup / gating
        50–149   Crit, Execute, Armor, Affinity
        150–199  Attacker / victim multipliers
        900      Safety clamps
   └─► Shields
        Capacity / Rechargeable / Reduction / Chain / Overheal
   └─► HealthSystem.ApplyDamage
   └─► POST Damage Rules (IPostDamageRule observers)
        Reflect, lifesteal, analytics, VFX

Heal Pipeline

Incoming HealContext
   └─► PRE Heal Rules
        Anti-heal, low-HP boosts, custom gates
   └─► Healing modifiers (IHealingModifier)
   └─► HealthSystem.TryHeal
   └─► POST Heal Rules
        Events, SFX, VFX, telemetry

PRE rules may: - mutate context values - cancel the operation - short-circuit the chain

POST rules: - observe only - never affect the applied result


🧮 Priority System

Every PRE rule declares an integer Priority.

  • Lower runs first
  • Higher runs later
  • POST observers always run after mutation
Family File Range
Damage DamageRulePriority.cs 0–999
Heal HealRulePriority.cs 0–999

Common Priority Bands (Guidance)

Range Purpose Examples
0–49 Setup / gating TeamRule
50–99 RNG / crit CritRule
90–120 Execute / armor / affinity ExecuteThresholdRule, ArmorRule, AffinityRule
140–199 Multipliers AttackerDamageMultiplierRule, DamageTakenMultiplierRule
900+ Late clamps & post effects DamageClampRule, ReflectStacksRule, LifestealRule

These ranges are conventions, not hard requirements, but following them keeps stacking behaviour predictable.


🗂 Folder Structure

Path Purpose
Abstractions/ Rule interfaces and deterministic RNG (IAttackerDamageModifier, IRng).
Core/ Context structs and hubs (DamageContext, HealContext, DamageRuleHub, HealRuleHub).
DamageRules/ PRE & POST damage rules (Team, Crit, Armor, Execute, Affinity, Clamp, Reflect, Lifesteal).
HealRules/ PRE & POST heal rules (AntiHeal, LowHPHealBoost, heal events, SFX, VFX).
Modifiers/ Healing modifier seams and helpers (IHealingModifier, HealingUtility) and rule-contributor components (e.g. ReflectBuff).
PostEffects/ POST-damage visual observers (DamageVFXPostRule).

🚀 Quick Start

  1. Add DamageRuleHub and/or HealRuleHub to a GameObject with HealthSystem.
  2. Add rule components from:
  3. DamageRules/
  4. HealRules/
  5. Assign rule Priority using constants from the Rules root.
  6. Add shields if needed — they run after PRE rules and before mutation.

No rules are required by default.


✍️ Authoring New Rules

Damage PRE Rule

public class MyRule : MonoBehaviour, IDamageRule
{
    public int Priority => DamageRulePriority.Armor + 10;

    public bool Apply(ref DamageContext ctx)
    {
        // mutate RawAmount / Multiplier / FlatDelta
        // return false or set ctx.Cancelled to stop the chain
        return true;
    }
}

Damage POST Rule

public class MyObserver : MonoBehaviour, IPostDamageRule
{
    public void OnDamageApplied(in DamageContext ctx)
    {
        if (ctx.FinalApplied <= 0) return;
        // VFX, procs, analytics, telemetry
    }
}

Heal PRE Rule

public class MyHealGate : MonoBehaviour, IHealRule
{
    public int Priority => HealRulePriority.AntiHeal + 10;

    public bool Apply(ref HealContext ctx)
    {
        if (/* condition */)
        {
            ctx.Cancelled = true;
            return false;
        }

        ctx.Multiplier *= 1.25f;
        return true;
    }
}

❌ What Rules Should Not Do

  • Do not mutate health directly
  • Do not bypass rule hubs
  • Do not depend on rule execution order implicitly
  • Do not encode networking or authority here
  • Do not mix UI or presentation logic into rules

Rules exist to stay small, deterministic, and composable.


🔗 See Also