15 KiB
15 KiB
08 — Health System (BPC_HealthSystem)
Purpose
Manages the player's health pool, damage application with type-based resistance, health regeneration, death state, and critical-health events. Serves as the central survivability component on the player character.
Dependencies
- Requires:
FL_GameUtilities(for safe interface casts),I_Damageable(interface on the owner),GI_GameFramework(for GamePhase queries) - Required By:
BPC_StressSystem(health below threshold triggers fear events),BPC_PlayerMetricsTracker(death tracking),WBP_HUD(health bar),AI_EnemyController(player health as awareness trigger),BPC_InteractionDetector(low-health checks) - Engine/Plugin Requirements: GameplayTags, DeveloperSettings
Class Info
| Property | Value |
|---|---|
| Parent Class | ActorComponent |
| Class Type | Blueprint Component |
| Asset Path | Content/Framework/Player/BPC_HealthSystem |
| Implements Interfaces | I_Damageable |
1. Enums
E_DamageType
| Value | Description |
|---|---|
Physical = 0 |
Bullets, blunt force, falls |
Arcane = 1 |
Magic, curse, psychic damage |
Fire = 2 |
Burning, environmental heat |
Poison = 3 |
Toxins, venom, chemical |
Fear = 4 |
Psychological damage (stress overflow) |
Environmental = 5 |
Traps, crushing, drowning |
True = 6 |
Bypasses all resistances, no reduction |
E_DeathState
| Value | Description |
|---|---|
Alive = 0 |
Normal gameplay |
Dying = 1 |
Downed state, waiting for recovery or final death |
Dead = 2 |
Character is dead, no further actions possible |
PermaDeath = 3 |
Save-scrubbing locked, forced reload |
2. Structs
S_DamageEvent
| Field | Type | Description |
|---|---|---|
BaseAmount |
Float |
Raw damage before resistance |
DamageType |
E_DamageType |
Category of damage |
Instigator |
Actor |
Who caused the damage |
HitLocation |
Vector |
World location of impact |
DamageTags |
GameplayTagContainer |
Additional contextual tags |
bIsCriticalHit |
Boolean |
True if this is a headshot / weak-point hit |
SourceDescription |
Text |
Human-readable source (for log / UI) |
S_DamageResistance
| Field | Type | Description |
|---|---|---|
DamageType |
E_DamageType |
Type this resistance applies to |
Multiplier |
Float |
1.0 = normal, 0.5 = half damage, 2.0 = double |
bIsStackable |
Boolean |
Can this resistance stack with others? |
3. Variables
Configuration (Instance Editable, Expose On Spawn)
| Variable | Type | Default | Category | Description |
|---|---|---|---|---|
MaxHealth |
Float |
100.0 |
Health Config |
Maximum hit points |
bRegenEnabled |
Boolean |
true |
Health Config |
Allow passive health regeneration |
RegenDelay |
Float |
3.0 |
Health Config |
Seconds after last damage before regen starts |
RegenRate |
Float |
2.0 |
Health Config |
HP recovered per second during regen |
RegenDelayUntil |
Float |
0.0 |
Health Config |
World time when regen can resume |
CriticalHealthPercent |
Float |
0.25 |
Health Config |
Below this fraction of MaxHealth, fire critical event |
DefaultResistances |
Array<S_DamageResistance> |
[] |
Health Config |
Base damage resistances for this character |
bEnableFriendlyFire |
Boolean |
false |
Health Config |
Can the player damage themselves? |
Internal (Private / Protected, No Expose)
| Variable | Type | Default | Category | Description |
|---|---|---|---|---|
CurrentHealth |
Float |
100.0 |
Health State |
Current HP, clamped [0..MaxHealth] |
DeathState |
E_DeathState |
Alive |
Health State |
Current death state |
ActiveDamageOverTime |
Map<FName, FTimerHandle> |
{} |
Health State |
Active DoT timers keyed by source name |
InvincibilityTimer |
FTimerHandle |
- |
Health State |
Timer handle for temporary invincibility |
RegenTimerHandle |
FTimerHandle |
- |
Health State |
Timer handle for regen tick |
bIsInvincible |
Boolean |
false |
Health State |
True during invincibility window |
LastDamageInstigator |
Actor |
None |
Health State |
Most recent damage source (for kill credit) |
LastDamageTime |
Float |
0.0 |
Health State |
World time of last damage taken |
Replicated (if multiplayer)
| Variable | Type | Condition | Description |
|---|---|---|---|
CurrentHealth |
Float |
RepNotify |
Replicated with OnRep handler for UI updates |
DeathState |
E_DeathState |
RepNotify |
Replicated death state |
4. Functions
Public Functions
ApplyDamage → S_DamageResult (custom struct)
- Description: Applies damage to the health pool after resistance calculation. Returns effective damage and any side effects.
- Parameters:
Param Type Description DamageEventS_DamageEventThe damage event to process - Blueprint Authority: Server (if MP), Any (single-player)
- Flow:
- Early-out if DeathState is Dead or bIsInvincible
- Calculate effective damage from BaseAmount * type resistances
- If damage > 0: reset RegenDelay timer, update CurrentHealth
- Fire OnDamageTaken dispatcher
- If CurrentHealth <= 0: call
HandleDeath - If CurrentHealth <= CriticalHealthPercent: fire OnHealthCritical
- Return damage result struct
ApplyHealing → Float (effective healing)
- Description: Adds health up to MaxHealth. Returns the amount actually healed.
- Parameters:
Param Type Description HealAmountFloatRaw healing value HealTagsGameplayTagContainerContextual tags - Blueprint Authority: Server (if MP), Any (single-player)
- Flow:
- Early-out if DeathState is Dead
- Clamp:
CurrentHealth = FMath::Min(MaxHealth, CurrentHealth + HealAmount) - Fire OnHealthChanged
- Return actual heal amount
GetHealthNormalised → Float [0.0 - 1.0]
- Description: Returns NormalisedCurrentHealth for UI widgets.
- Flow:
Return CurrentHealth / MaxHealth
SetMaxHealth → void
- Description: Changes MaxHealth and optionally heals to match new ratio or clamps current.
- Parameters:
Param Type Description NewMaxFloatNew maximum health value bMaintainRatioBooleanIf true, scale CurrentHealth to same ratio - Flow:
- Clamp NewMax to >= 1.0
- If bMaintainRatio:
CurrentHealth = (CurrentHealth / MaxHealth) * NewMax - Else:
CurrentHealth = FMath::Min(CurrentHealth, NewMax) - Set MaxHealth = NewMax
- Fire OnHealthChanged
KillInstant → void
- Description: Bypasses all resistances, immediately sets health to 0 and triggers death.
- Parameters:
Param Type Description InstigatorActorWho caused the kill DeathReasonFTextReason displayed on death screen - Blueprint Authority: Server (if MP), Any (single-player)
- Flow:
- Set CurrentHealth = 0
- Set DeathState = Dying
- Fire OnHealthChanged with CurrentHealth = 0
- Call HandleDeath with bInstant = true
ApplyDamageOverTime → void
- Description: Applies periodic damage with tick interval and total duration.
- Parameters:
Param Type Description DamagePerTickFloatDamage applied each interval TickIntervalFloatSeconds between ticks TotalDurationFloatTotal seconds the DoT lasts DamageTypeE_DamageTypeType of each damage tick SourceNameFNameUnique name to prevent stacking same DoT InstigatorActorWho applied the DoT - Flow:
- If SourceName already exists in ActiveDamageOverTime, clear existing timer
- Create a Timer by Event with loop = TickInterval
- On each tick: build S_DamageEvent, call ApplyDamage
- After TotalDuration: clear timer, remove from ActiveDamageOverTime
IsAlive → Boolean
- Flow:
Return DeathState == Alive
AddResistance → void
- Parameters:
Param Type Description NewResistanceS_DamageResistanceResistance to add or update - Flow: If resistance for this DamageType exists, update Multiplier; if not, add to array.
RemoveResistance → void
- Parameters:
Param Type Description DamageTypeE_DamageTypeType to remove - Flow: Remove all entries matching this DamageType from resistance array.
Protected / Private Functions
HandleDeath → void
- Description: Internal death processing. Sets state, fires dispatchers, notifies GameMode.
- Parameters:
Param Type Description bInstantBooleanIf true, skip dying state go straight to dead - Flow:
- Set DeathState = (bInstant ? Dead : Dying)
- Clear all timers (regen, DoT, invincibility)
- Fire OnDeath with DeathState
- Call
I_Damageable::OnOwnerDiedon owner if interface exists - Get
GM_CoreGameModeviaGetWorld()->GetAuthGameMode() - Call
GM_CoreGameMode::HandlePlayerDead(LastDamageInstigator)
RegenTick → void
- Description: Timer callback — applies one tick of regeneration.
- Flow:
- If CurrentHealth >= MaxHealth: stop regen timer, return
CurrentHealth = FMath::Min(MaxHealth, CurrentHealth + RegenRate * TickInterval)- Fire OnHealthChanged
- If CurrentHealth >= MaxHealth: stop regen timer
5. Event Dispatchers
| Dispatcher | Parameters | Bind Access | Description |
|---|---|---|---|
OnHealthChanged |
float OldHealth, float NewHealth, float Delta |
Public |
Fired when health changes by any amount |
OnDamageTaken |
S_DamageEvent DamageEvent, float EffectiveDamage |
Public |
Fired each time damage is applied |
OnHealingReceived |
float HealAmount, GameplayTagContainer HealTags |
Public |
Fired each time healing is applied |
OnHealthCritical |
float CurrentHealth, float MaxHealth |
Public |
Fired when health drops below critical threshold |
OnDeath |
E_DeathState DeathState, Actor Instigator |
Public |
Fired when character dies or enters dying state |
OnDeathStateChanged |
E_DeathState OldState, E_DeathState NewState |
Public |
Fired on any death state transition |
OnInvincibilityChanged |
bool bIsInvincible |
Public |
Fired when invincibility state toggles |
6. Overridden Events / Custom Events
Event: BeginPlay
- Description: Initializes health, loads saved health if persisting, starts regen timer if enabled.
- Flow:
- Set CurrentHealth = MaxHealth
- Check if owner implements
I_Persistable - If yes, try to load saved health value
- If bRegenEnabled: start regen timer with initial delay = 0
- Bind to any relevant GamePhase change dispatchers
Event: OnComponentDestroyed
- Description: Clean up all active timers.
- Flow:
- Clear all TimerHandles (Invincibility, Regen, all DoT timers)
- Clear ActiveDamageOverTime map
Custom Event: ApplyTemporaryInvincibility
- Parameters:
float Duration - Description: Grants brief invincibility, often used after respawn or dodge.
- Flow:
- Set bIsInvincible = true
- Fire OnInvincibilityChanged(true)
- Start timer for Duration
- On timer end: set bIsInvincible = false, fire OnInvincibilityChanged(false)
7. Blueprint Graph Logic Flow
flowchart TD
A[ApplyDamage called] --> B{DeathState == Dead?}
B -->|Yes| C[Return 0 damage]
B -->|No| D{bIsInvincible?}
D -->|Yes| C
D -->|No| E[Calculate resistance multiplier]
E --> F[EffectiveDamage = Base * Multiplier]
F --> G[CurrentHealth -= EffectiveDamage]
G --> H[Reset RegenDelay timer]
H --> I[Fire OnDamageTaken]
I --> J{CurrentHealth <= 0?}
J -->|Yes| K[HandleDeath]
J -->|No| L{CurrentHealth <= CriticalThreshold?}
L -->|Yes| M[Fire OnHealthCritical]
L -->|No| N[Fire OnHealthChanged]
K --> O[Fire OnDeath dispatcher]
K --> P[Notify GameMode: HandlePlayerDead]
K --> Q[Clear all timers]
M --> N
8. Communication Matrix
| Who Talks | How | What Is Sent |
|---|---|---|
BPC_HealthSystem |
Dispatcher |
OnHealthChanged -> WBP_HUD, BPC_StressSystem |
BPC_HealthSystem |
Dispatcher |
OnDeath -> GM_CoreGameMode, SS_SaveSystem, BPC_PlayerMetricsTracker |
BPC_HealthSystem |
Dispatcher |
OnDamageTaken -> BPC_StressSystem, BPC_AdaptiveEnvironment |
BPC_HealthSystem |
Interface |
I_Damageable.Execute_ApplyDamage route |
BPC_HealthSystem |
Interface |
I_Persistable for save/load of CurrentHealth |
External (Weapons, Traps) |
Interface |
I_Damageable -> ApplyDamage |
BPC_HealthSystem |
Direct |
GM_CoreGameMode.HandlePlayerDead on death |
BPC_HealthSystem |
Subsystem |
GI_GameFramework.GetGamePhase for phase-restricted actions |
9. Validation / Testing Checklist
- ApplyDamage with all E_DamageType values produces correct resistance-modified damage
- Health never drops below 0 or exceeds MaxHealth
- Regeneration does not start while RegenDelay timer is active
- Regeneration stops when health reaches MaxHealth
- Death sets DeathState properly and fires OnDeath exactly once
- Temporary invincibility blocks all damage for its duration
- Damage-over-time correctly applies ticks and cleans up timer on death
- Critical health event fires only when crossing below threshold from above
- Edge case: ApplyHealing on a dead character returns 0 with no dispatchers
- Edge case: ApplyDamage with negative BaseAmount is treated as 0
- Edge case: Multiple simultaneous DoT sources with same SourceName do not stack
- All dispatchers are cleaned up on component destroy
10. Reuse Notes
- This component can be used on AI characters and enemy pawns by exposing the same configuration variables.
- For enemies, consider disabling regen (bRegenEnabled = false) unless a specific healing mechanic exists.
- The resistance system supports temporary buffs via AddResistance / RemoveResistance at runtime.
- If implementing a downed / bleeding state, extend E_DeathState with custom values and add a recovery timer check in HandleDeath.
- Damage numbers displayed as floating text should bind to OnDamageTaken.
- For multiplayer: all ApplyDamage logic runs on Server; clients receive repnotified CurrentHealth and fire local cosmetic dispatchers only.
Blueprint Spec: Health System. Conforms to TEMPLATE.md v1.0 — part of the UE5 Modular Game Framework.