Skip to content

⚔️ Health — Damage Rules

This folder contains all damage-side rule components that plug into DamageRuleHub.

Damage rules define how incoming damage is shaped before it reaches HealthSystem, and how systems react after damage has been applied.

All damage rules are: - Modular - Order-driven via Priority - Authority-agnostic - Composable per prefab


📦 What Damage Rules Are (and Aren’t)

Damage rules are - Deterministic pipeline steps - Context mutators (PRE) or observers (POST) - The primary extension surface for combat math

Damage rules are not - Health state owners - Networking or replication logic - UI or presentation systems

They sit between input and mutation — never replacing HealthSystem.


🧩 Basic Usage

  1. Add a DamageRuleHub to the GameObject with HealthSystem
  2. Add any rule components from this folder
  3. Rules implementing:
  4. IDamageRule run in PRE
  5. IPostDamageRule run in POST
  6. Lower Priority runs first
  7. POST rules always run after shields and final damage resolution

No damage rules are required by default.


🧮 Common Priority Bands

Priority Purpose Example Rules
40 Team gating TeamRule
50–99 RNG / crit CritRule
90–120 Execute / armor / affinity ExecuteThresholdRule, ArmorRule, AffinityRule
140–199 Multipliers AttackerDamageMultiplierRule, DamageTakenMultiplierRule
900+ Safety & post DamageClampRule, ReflectStacksRule, LifestealRule

See DamageRulePriority.cs for canonical constants.


⚙️ PRE Damage Rules

PRE rules mutate DamageContext before shields and health mutation.

They may: - modify RawAmount, Multiplier, or FlatDelta - set ctx.Cancelled = true - return false to short-circuit the chain


TeamRule (priority 40)

  • Cancels same-team damage unless allowFriendlyFire is enabled
  • Applies friendlyFireScale or enemyScale
  • Uses DamageContext.TeamId and victim ITeamProvider

CritRule (priority 50)

  • Performs a critical hit roll
  • Adds DamageTag.Crit on success
  • Multiplies damage via critMultiplier
  • Supports:
  • deterministic seeds
  • Unity RNG
  • external RNG providers
  • forced crits for testing

ExecuteThresholdRule (priority 90)

When victim HP% ≤ threshold:

  • ctx.BypassShields = true
  • ctx.BypassRules |= RuleBypass.Armor
  • ctx.Tags |= DamageTag.True

Ensures true-damage behaviour before armor and affinity.


ArmorRule (priority 100)

  • Flat and percent mitigation
  • Applied only to selected DamageTag masks
  • Mutates ctx.RawAmount
  • Skipped when RuleBypass.Armor is set

AffinityRule (priority 120)

  • Queries all victim-side IDamageAffinity providers
  • Multiplies all returned opinions:
  • 0 → immunity (cancel)
  • <1 → resist
  • >1 → weak
  • Skipped when RuleBypass.Affinity is set

AttackerDamageMultiplierRule (priority 140)

  • Queries attacker for all IAttackerDamageModifier components
  • Multiplies results into ctx.Multiplier
  • If final product = 0, the hit is cancelled

DamageTakenMultiplierRule (priority 150)

  • Victim-side, stackable multiplier rule
  • Runtime API:
  • Push(multiplier)
  • Pop(multiplier)
  • Clear()
  • Product of all stacks is applied
  • Safety-clamped via minFactor / maxFactor
  • 0 cancels the hit

DamageClampRule (priority 900)

Final guardrail before shields.

  • Clamps post-rule damage into [minAfterAll, maxAfterAll]
  • Adjusts via FlatDelta to preserve rule ordering
  • Prevents runaway math or negative damage

💥 POST Damage Rules

POST rules never mutate damage. They react to finalized results only.

ctx.FinalApplied is guaranteed to be valid.


ReflectStacksRule (priority 980)

  • Stack-based reflect collector
  • Used with ReflectBuff
  • Sums reflect contributions, clamped by maxTotal
  • Reflects % of final applied damage
  • Skips reflected hits to avoid loops
  • Optional skip for true damage

DamageReflectRule (priority 990)

  • Simple one-off reflect rule
  • Reflects % of final applied damage
  • Optional reflectOnTrueDamage
  • Tags reflected hits with DamageTag.Reflect

LifestealRule (priority 999)

  • Heals attacker for % of final applied damage
  • Uses attacker’s IHealthWriter
  • Skips when:
  • FinalApplied <= 0
  • (optionally) hit was tagged True

🧠 Design Guarantees

  • PRE rules mutate context; POST rules observe results
  • Rule order is explicit and deterministic
  • Rules never mutate health directly
  • Different prefabs may have entirely different rule stacks
  • Adding/removing rules is safe at runtime

❌ What Damage Rules Should Not Do

  • Do not modify health directly
  • Do not assume a specific rule order beyond Priority
  • Do not embed authority or networking logic
  • Do not mix UI or presentation logic into rules

Damage rules exist to stay small, predictable, and composable.


🔗 See Also