Files
UE5-Modular-Game-Framework/docs/blueprints/02-player/10_BPC_StressSystem.md
Lefteris Notas 411edea8ce add blueprints
2026-05-19 13:22:27 +03:00

14 KiB

10 — Stress / Sanity System (BPC_StressSystem)

Purpose

Tracks the player's psychological state via a stress meter that accumulates from environmental horror, enemy proximity, health deficits, and narrative events. Drives visual distortion, audio hallucinations, gameplay penalties, and narrative branching based on sanity thresholds.

Dependencies

  • Requires: BPC_HealthSystem (low health triggers stress), GI_GameFramework (phase checks)
  • Required By: BPC_PlayerController (shader/audio effects), WBP_HUD (stress vignette), NarrativeManager (sanity-gated content), BPC_AdaptiveEnvironment (world distortion)
  • Engine/Plugin Requirements: GameplayTags, Timers, Material Parameter Collections (for post-process effects)

Class Info

Property Value
Parent Class ActorComponent
Class Type Blueprint Component
Asset Path Content/Framework/Player/BPC_StressSystem
Implements Interfaces None

1. Enums

E_StressTier

Value Description
Calm = 0 No stress effects
Uneasy = 1 Subtle audio cues, slight FOV shift
Distressed = 2 Visual noise, heartbeat audio, reduced stamina regen
Panicked = 3 Heavy distortion, screen shake, movement penalty
Terrified = 4 Hallucinations, audio distortion, random input inversion
Catatonic = 5 Brief freeze/stumble, forced slow walk

E_StressSource

Value Description
EnemyProximity = 0 Nearby hostile presence
EnvironmentalHorror = 1 Gore, darkness, unsettling areas
HealthDeficit = 2 Low health triggers fear
NarrativeEvent = 3 Scripted story beats
Supernatural = 4 Ghosts, paranormal activity
Isolation = 5 Long periods alone
TraumaTrigger = 6 PTSD flashback spots

2. Structs

S_StressThresholds

Field Type Description
UneasyThreshold Float Stress value to enter Uneasy tier
DistressedThreshold Float Stress value to enter Distressed
PanickedThreshold Float Stress value to enter Panicked
TerrifiedThreshold Float Stress value to enter Terrified
CatatonicThreshold Float Stress value to enter Catatonic

S_StressSourceData

Field Type Description
SourceType E_StressSource Category of source
CurrentIntensity Float Current contribution from this source [0..Max]
MaxIntensity Float Maximum contribution this source can apply
DecayRate Float How fast this source fades when no longer active
SourceTags GameplayTagContainer Contextual tags for filtering
SourceIdentifier FName Unique name for this specific source instance

S_StressEvent

Field Type Description
Amount Float Stress to add or remove
SourceType E_StressSource Category of source
Instigator Actor What caused the stress change
bIsInstant Boolean If true, bypasses buildup curve
EventTags GameplayTagContainer Additional context

3. Variables

Configuration (Instance Editable, Expose On Spawn)

Variable Type Default Category Description
MaxStress Float 100.0 Stress Config Maximum stress value
Thresholds S_StressThresholds (15, 30, 50, 75, 90) Stress Config Threshold values for each tier
PassiveDecayRate Float 2.0 Stress Config Stress lost per second when in calm area
PanicDecayDelay Float 5.0 Stress Config Seconds at max tier before forced decay begins
bEnableHallucinations Boolean true Stress Config Allow visual/audio hallucinations at high stress
bCanGoCatatonic Boolean true Stress Config Allow catatonic freeze state
bStressAffectsMovement Boolean true Stress Config Apply movement penalties from stress
HealthDeficitMultiplier Float 1.5 Stress Config How much low health amplifies incoming stress

Internal (Private / Protected, No Expose)

Variable Type Default Category Description
CurrentStress Float 0.0 Stress State Current accumulated stress
CurrentTier E_StressTier Calm Stress State Current stress tier
ActiveSources Map<FName, S_StressSourceData> {} Stress State Currently contributing stress sources
StressDecayTimer FTimerHandle - Stress State Timer for passive decay tick
bIsInSafeZone Boolean true Stress State True when player is in a calm area
LastStressTime Float 0.0 Stress State World time of last stress change
bHallucinationsActive Boolean false Stress State Whether hallucinations are currently triggered
TierEntryTimes Map<E_StressTier, float> {} Stress State World time when each tier was entered

Replicated (if multiplayer)

Variable Type Condition Description
CurrentStress Float RepNotify Replicated for UI and effects
CurrentTier E_StressTier RepNotify Replicated tier state

4. Functions

Public Functions

AddStressFloat (actual stress added)

  • Description: Adds stress from a source. Returns amount actually added.
  • Parameters:
    Param Type Description
    Event S_StressEvent Stress event to apply
  • Blueprint Authority: Any
  • Flow:
    1. If CurrentTier == Catatonic and !Event.bIsInstant: return 0 (immune at max)
    2. If HealthDeficitMultiplier > 1.0 and health < 50%: scale amount
    3. If Event.bIsInstant: CurrentStress += Event.Amount
    4. Else: add/update ActiveSources with Event data, recalculate from sources
    5. Clamp CurrentStress to [0, MaxStress]
    6. Update CurrentTier
    7. Fire OnStressChanged
    8. If tier changed: fire OnStressTierChanged
    9. If tier >= Panicked: start tick for hallucinations
    10. Return delta

RemoveStressFloat

  • Description: Reduces stress directly or removes a specific source.
  • Parameters:
    Param Type Description
    Amount Float Amount to reduce
    SourceIdentifier FName Optional — if set, remove this source entirely
  • Flow:
    1. If SourceIdentifier set: remove from ActiveSources, recalculate total
    2. Else: CurrentStress = FMath::Max(0, CurrentStress - Amount)
    3. Update CurrentTier
    4. Fire OnStressChanged
    5. If tier changed: fire OnStressTierChanged

AddStressSourcevoid

  • Parameters:
    Param Type Description
    SourceData S_StressSourceData Source configuration to add
  • Flow:
    1. If SourceData.SourceIdentifier already in ActiveSources: update MaxIntensity and DecayRate
    2. Else: add new entry
    3. Recalculate total stress from all sources

RemoveStressSourcevoid

  • Parameters:
    Param Type Description
    SourceIdentifier FName Source to remove
  • Flow:
    1. Remove from ActiveSources
    2. Recalculate total stress

SetSafeZonevoid

  • Parameters:
    Param Type Description
    bSafe Boolean Whether player is in a safe zone
  • Flow:
    1. bIsInSafeZone = bSafe
    2. If bSafe: begin passive decay
    3. If !bSafe: stop passive decay
    4. Fire OnSafeZoneChanged

GetStressNormalisedFloat [0.0 - 1.0]

  • Flow: Return CurrentStress / MaxStress

GetTierDurationFloat

  • Parameters:
    Param Type Description
    Tier E_StressTier Which tier to query
  • Description: Returns how long the player has been in the given tier.
  • Flow: If TierEntryTimes contains Tier: return CurrentWorldTime - TierEntryTimes[Tier]; else return 0

Protected / Private Functions

UpdateTiervoid

  • Flow:
    1. OldTier = CurrentTier
    2. Check CurrentStress against Thresholds in descending order
    3. NewTier = matching tier (highest threshold exceeded)
    4. If NewTier != OldTier:
      • CurrentTier = NewTier
      • Record TierEntryTimes[NewTier] = CurrentWorldTime
      • Fire OnStressTierChanged

RecalculateFromSourcesvoid

  • Flow:
    1. Total = 0.0
    2. For each ActiveSource: Total += Source.CurrentIntensity
    3. CurrentStress = FMath::Clamp(Total, 0, MaxStress)
    4. UpdateTier
    5. Fire OnStressChanged

PassiveDecayTickvoid

  • Flow:
    1. If bIsInSafeZone or CurrentTier < Distressed or CurrentStress <= 0: return
    2. DecayAmount = PassiveDecayRate * 0.1
    3. CurrentStress = FMath::Max(0, CurrentStress - DecayAmount)
    4. Fire OnStressChanged

HandleHallucinationCheckvoid

  • Flow:
    1. If CurrentTier < Terrified or !bEnableHallucinations:
      • If bHallucinationsActive: StopHallucinations
      • return
    2. Random chance each tick (e.g., 5% per second at Terrified)
    3. If roll succeeds: trigger random hallucination event
    4. Fire OnHallucinationTriggered

5. Event Dispatchers

Dispatcher Parameters Bind Access Description
OnStressChanged float OldStress, float NewStress Public Fired on any stress change
OnStressTierChanged E_StressTier OldTier, E_StressTier NewTier Public Fired when stress tier changes
OnStressSourceAdded FName SourceIdentifier, E_StressSource SourceType Public Fired when a new stress source is added
OnStressSourceRemoved FName SourceIdentifier Public Fired when a stress source is removed
OnSafeZoneChanged bool bIsSafe Public Fired when entering/leaving safe zone
OnHallucinationTriggered E_HallucinationType Type Public Fired when a hallucination event occurs
OnCatatonicStateEntered none Public Fired when entering Catatonic tier
OnStressRecovered none Public Fired when stress drops back to Calm

6. Overridden Events / Custom Events

Event: BeginPlay

  • Description: Initialises stress system, binds to health component if available.
  • Flow:
    1. CurrentStress = 0.0
    2. CurrentTier = Calm
    3. Find BPC_HealthSystem on owner
    4. If found: bind OnDamageTaken to a handler that adds stress for damage taken
    5. Start passive decay timer (interval 0.1s)

Custom Event: OnDamageTakenHandler

  • Parameters: S_DamageEvent DamageEvent, float EffectiveDamage
  • Description: Stress response to taking damage.
  • Flow:
    1. StressAmount = EffectiveDamage * 0.5
    2. If DamageType == Fear: StressAmount *= 2.0
    3. Build S_StressEvent with SourceType = HealthDeficit
    4. Call AddStress

Custom Event: ForcePanic

  • Parameters: float Duration
  • Description: Forcibly raises stress to Panicked tier for a duration, used by narrative events.
  • Flow:
    1. CurrentStress = Thresholds.PanickedThreshold
    2. UpdateTier
    3. Start timer for Duration
    4. On timer end: gradually return stress to previous level

7. Blueprint Graph Logic Flow

flowchart TD
    A[AddStress called] --> B{CurrentTier == Catatonic?}
    B -->|Yes| C[Return 0]
    B -->|No| D[Apply HealthDeficitMultiplier]
    D --> E[Update ActiveSources map]
    E --> F[Recalculate total stress]
    F --> G[Clamp to 0..MaxStress]
    G --> H{NewTier == OldTier?}
    H -->|No| I[Update CurrentTier]
    I --> J[Record tier entry time]
    J --> K[Fire OnStressTierChanged]
    H -->|Yes| L[Skip tier update]
    K --> M{Tier >= Panicked?}
    M -->|Yes| N[Start hallucination ticker]
    M -->|No| O{Stress dropped below Distressed?}
    O -->|Yes| P[Stop hallucinations]
    O -->|No| Q[Continue passive decay]
    N --> R[Fire OnStressChanged]
    P --> R
    Q --> R

8. Communication Matrix

Who Talks How What Is Sent
BPC_StressSystem Dispatcher OnStressTierChanged -> PC_PlayerController (post-process), ABP_GASP (anim), WBP_HUD (vignette)
BPC_StressSystem Dispatcher OnHallucinationTriggered -> BP_HallucinationManager, BP_AudioManager
BPC_StressSystem Dispatcher OnStressChanged -> WBP_HUD (stress bar)
BPC_StressSystem Dispatcher OnCatatonicStateEntered -> BPC_PlayerController (input lockdown)
External (Enemies) Direct Calls AddStressSource on detection
External (BP_DeathZone) Direct Calls AddStress via interface
BPC_HealthSystem Listener Binds to OnDamageTaken for health-deficit stress
BPC_StressSystem Dispatcher OnSafeZoneChanged -> BPC_AdaptiveAtmosphere

9. Validation / Testing Checklist

  • Stress accumulates from multiple sources and sums correctly
  • Stress decays passively when in safe zone or above Distressed
  • Tier transitions fire exactly once per threshold crossing
  • Catatonic tier blocks further stress accumulation
  • Hallucinations trigger randomly at Terrified tier and stop below it
  • Stress sources with identifiers are properly tracked and removed
  • Edge case: Stress added while at MaxStress clamps and does not overflow
  • Edge case: RemoveStress with source identifier removes only that source
  • Edge case: ForcePanic temporarily locks tier and restores after duration
  • All timers clean up on component destroy

10. Reuse Notes

  • Can be used on NPCs for a simplified "morale" system (remove hallucination features).
  • Stress tiers can gate narrative content — questgivers behave differently based on player stress.
  • Hallucination types can be configured via Data Asset (DA_HallucinationConfig).
  • For multiplayer: Stress is server-authoritative; clients receive tier changes for local effects.
  • The HealthDeficitMultiplier creates a death spiral feel — use carefully for horror pacing.
  • Safe zones double as narrative checkpoints and stress recovery areas.

Blueprint Spec: Stress/Sanity System. Conforms to TEMPLATE.md v1.0 — part of the UE5 Modular Game Framework.