# 02 — Player State & Embodiment Systems (Systems 08-15) **Category Purpose:** These 8 systems form the player character's core simulation layer — health, stamina, stress, movement, hiding, first-person body, camera, and metrics tracking. They cascade: health affects stress, stress affects movement, movement affects camera. All feed into the animation system (ABP_GASP) via event dispatchers. --- ## System Index | # | System | Asset Type | Role | |---|--------|-----------|------| | 08 | `BPC_HealthSystem` | Component | Health pool, damage resistance, death trigger, healing | | 09 | `BPC_StaminaSystem` | Component | Stamina pool, sprint/action drain, exhaustion states | | 10 | `BPC_StressSystem` | Component | Psychological stress, tiers (Calm→Catatonic), hallucinations | | 11 | `BPC_MovementStateSystem` | Component | Movement mode + posture tracking; GASP liaison; footsteps | | 12 | `BPC_HidingSystem` | Component | Hide/peek/breath-hold; LOS checks; stress decay while hidden | | 13 | `BPC_EmbodimentSystem` | Component | First-person body mesh, arm IK, visibility modes, overlays | | 14 | `BPC_CameraStateLayer` | Component | Camera FOV/offset/rotation layers for all states; shake system | | 15 | `BPC_PlayerMetricsTracker` | Component | Player metrics: accuracy, deaths, playstyle ratios | --- ## Category Data Flow — The Cascade ``` ┌──────────────────────────────────────────────────────────────────────┐ │ PLAYER SIMULATION CASCADE │ │ │ │ Combat Damage │ │ ↓ │ │ BPC_HealthSystem │ │ ↓ dispatchers: OnDamageTaken → │ │ BPC_StressSystem ←── also fed by: enemy proximity, environment, │ │ ↓ dispatchers: OnStressTierChanged → narrative events │ │ BPC_CameraStateLayer (FOV pulse, chromatic aberration) │ │ BPC_MovementStateSystem (movement penalties at high stress) │ │ ↓ dispatchers: OnMovementModeChanged → │ │ BPC_StaminaSystem (regen blocked while sprinting) │ │ ABP_GASP (animation selection) │ │ ↓ dispatchers: OnSprintStateChanged → │ │ BPC_StaminaSystem (continuous drain during sprint) │ │ │ │ BPC_HidingSystem ←── interacts with both: │ │ ↓ stress decay while hidden │ │ ↓ camera constrained while hiding │ │ ↓ movement disabled while inside spot │ │ │ │ BPC_EmbodimentSystem ←── visual overlays from damage/water │ │ BPC_PlayerMetricsTracker ←── records everything for analytics │ └──────────────────────────────────────────────────────────────────────┘ ``` --- ## 08 — BPC_HealthSystem: Health, Damage & Death **What It Does:** The central survivability component. Manages a health pool (MaxHealth → CurrentHealth), applies damage with type-based resistance calculation, handles healing, tracks death state (Alive → Dying → Dead → PermaDeath), and fires dispatchers that cascade to stress, UI, and death handling systems. **How It Works Internally:** **Damage Application Flow:** 1. `ApplyDamage(DamageEvent)` called by weapon, trap, or environment 2. Early-out checks: Is DeathState == Dead? Is bIsInvincible? → return 0 3. Calculate resistance: look up `S_DamageResistance` for `DamageType`, multiply `BaseAmount` by `Multiplier` 4. `CurrentHealth -= EffectiveDamage` 5. Reset `RegenDelay` timer (regen starts after delay from last damage) 6. Fire `OnDamageTaken` → `BPC_StressSystem` reacts, HUD updates 7. Check thresholds: if `CurrentHealth <= 0` → `HandleDeath()`; if `CurrentHealth <= CriticalHealthPercent * MaxHealth` → fire `OnHealthCritical` 8. If DamageType is `Fear` → stress multiplier on `BPC_StressSystem` **Resistance System:** - Default resistances stored as array of `S_DamageResistance` (type → multiplier) - 7 damage types: Physical, Arcane, Fire, Poison, Fear, Environmental, True (bypasses all) - `AddResistance()` / `RemoveResistance()` at runtime for buffs/debuffs **Death States:** - `Alive` → normal; `Dying` → downed, waiting for recovery/final death; `Dead` → no actions; `PermaDeath` → save-scrubbing locked - On death: clear all timers (regen, DoT, invincibility), fire `OnDeath`, notify `GM_CoreGameMode.HandlePlayerDead()` **Health Regeneration:** - `RegenDelay` seconds after last damage before regen starts - Timer ticks `RegenRate` HP per second until `CurrentHealth >= MaxHealth` - Blocked by death state **Implementation Patterns:** - `GetHealthNormalised()` returns 0.0-1.0 for UI bars — smooth interpolation, never raw polling - `KillInstant()` bypasses everything for scripted deaths - `ApplyDamageOverTime()` creates a looping timer with unique SourceName to prevent stacking - `SetMaxHealth(NewMax, bMaintainRatio)` scales CurrentHealth proportionally or clamps - All dispatchers fire on authority; UI binds and reacts **Integration Points:** - **Listens to:** Nothing directly (called by damagers) - **Broadcasts:** `OnHealthChanged`, `OnDamageTaken`, `OnHealthCritical`, `OnDeath`, `OnDeathStateChanged`, `OnInvincibilityChanged` - **Key consumers:** `BPC_StressSystem` (damage → stress), `WBP_HUD` (health bar), `GM_CoreGameMode` (death), `BPC_HitReactionSystem` (stagger) **Edge Cases:** - Invincibility blocks all damage for its duration (used after respawn/dodge) - DoT with same SourceName replaces existing (no stacking) - ApplyHealing on dead character returns 0, no dispatchers - Negative damage treated as 0 --- ## 09 — BPC_StaminaSystem: Stamina Pool & Exhaustion **What It Does:** Manages a stamina resource that drains during sprinting, jumping, climbing, dodging, and special attacks. Enforces minimum stamina thresholds per action, applies exhaustion penalties (Normal → Low → Exhausted), and coordinates with MovementState for regen blocking while moving. **How It Works Internally:** **Drain System:** - Each `E_StaminaActionType` (Sprint, Jump, Climb, SpecialAttack, Dodge, Environment) has a `S_StaminaDrainRate` config: `DrainPerSecond` (continuous), `DrainFlat` (one-shot), `MinStamina` (threshold to perform), `CooldownAfterUse` - `DrainStamina(ActionType)` checks: cooldown active? sufficient stamina? → deduct, update exhaustion, start regen delay - `StartContinuousDrain(ActionType)` for sprint/climb — timer runs at 0.1s intervals - `StopContinuousDrain(ActionType)` stops when action ends **Exhaustion States:** - `Normal` (full operation) → `Low` (below LowThreshold, heavy breathing) → `Exhausted` (below ExhaustedThreshold, cannot sprint, slow regen) - `UpdateExhaustionState()` checks `CurrentStamina / MaxStamina` against configured thresholds - Transition downward is instant; recovery is gradual - `OnExhausted` dispatcher → player controller shows feedback, sprint blocked **Regeneration:** - `RegenSettings` configures: `RegenRate`, `RegenDelay`, `ExhaustedRegenRate`, `ExhaustedRegenDelay`, `RegenBlockedWhileMoving` - If `RegenBlockedWhileMoving`: listens to `BPC_MovementStateSystem.OnMovementModeChanged`, pauses regen when speed > walk - `BlockRegen(Duration)` for external debuffs **Implementation Patterns:** - `CanAffordAction(ActionType)` checks both stamina level and cooldown before allowing - Per-action cooldowns prevent dodge/jump spamming - `RestoreStamina(Amount, Tags)` for potions/resting - `SetMaxStamina(NewMax, bMaintainRatio)` scales current proportionally **Integration Points:** - **Called by:** `PC_PlayerController` (sprint, jump, dodge input) - **Broadcasts:** `OnStaminaChanged` → `WBP_HUD`, `OnExhausted` → movement/audio, `OnExhaustionStateChanged` → `ABP_GASP` (breathing anim) - **Listens to:** `BPC_MovementStateSystem.OnMovementModeChanged` (regen blocking) - **Key consumers:** `BPC_MovementStateSystem` (exhausted blocks sprint), `BPC_CameraStateLayer` (exhaustion effects) **Edge Cases:** - Multiple continuous drains stack independently - DrainStamina during cooldown returns false without side effects - MaxStamina change with bMaintainRatio preserves current percentage - All timers cleaned on component destroy --- ## 10 — BPC_StressSystem: Psychological Stress & Sanity **What It Does:** Simulates the player's psychological state via an accumulating stress meter. Multiple stress sources contribute simultaneously (enemy proximity, environmental horror, health deficit, narrative events, supernatural, isolation). Stress tiers (Calm→Uneasy→Distressed→Panicked→Terrified→Catatonic) drive visual distortion, audio hallucinations, movement penalties, and narrative branching. **How It Works Internally:** **Multi-Source Architecture:** - `ActiveSources` map (FName identifier → `S_StressSourceData`) tracks all contributing sources - Each source has: `CurrentIntensity`, `MaxIntensity`, `DecayRate`, `SourceTags` - `AddStressSource()` adds or updates a source; `RemoveStressSource()` removes it - `RecalculateFromSources()` sums all source intensities and updates total **Stress Events:** - `AddStress(Event)` can be instant (`bIsInstant`) or gradual (adds to source system) - `HealthDeficitMultiplier` amplifies incoming stress when health < 50% — creates death spiral feel - `OnDamageTakenHandler` converts damage amount to stress (Fear damage type gets 2x multiplier) - `ForcePanic(Duration)` temporarily forces Panicked tier for narrative use **Tier System:** - 6 tiers with configurable thresholds: `Uneasy` (subtle audio), `Distressed` (visual noise, heartbeat), `Panicked` (heavy distortion, movement penalty), `Terrified` (hallucinations, input inversion), `Catatonic` (freeze/stumble) - `UpdateTier()` compares `CurrentStress` against threshold values - Tier transitions fire `OnStressTierChanged` — systems react to tier, not raw stress value - `TierEntryTimes` map records when each tier was entered for duration tracking **Hallucination System:** - At `Terrified` tier: `HandleHallucinationCheck()` rolls random chance each tick - Triggers random hallucination events (visual, audio, dialogue) - Stops when tier drops below `Distressed` **Safe Zones & Recovery:** - `SetSafeZone(bSafe)` toggles passive decay - `PassiveDecayTick()` slowly reduces stress when in safe area or below Distressed - `OnSafeZoneChanged` dispatcher → atmosphere system adjusts **Implementation Patterns:** - Sources with identifiers enable precise tracking (e.g., "Enemy_Guard_12", "Area_Basement") - `GetTierDuration(Tier)` for narrative systems to query how long player has been panicked - At Catatonic, further stress accumulation is blocked (immune at max) - All effects driven by tier dispatchers, never by polling **Integration Points:** - **Called by:** Enemies (proximity), environment triggers, `BPC_HealthSystem` (damage handler) - **Broadcasts:** `OnStressTierChanged` → `BPC_CameraStateLayer` (post-process), `ABP_GASP` (anim), `WBP_HUD` (vignette), `OnHallucinationTriggered` → hallucination/audio managers, `OnCatatonicStateEntered` → input lockdown - **Listens to:** `BPC_HealthSystem.OnDamageTaken` **Edge Cases:** - Catatonic blocks all non-instant stress additions - Stress clamping prevents overflow past MaxStress - Source with matching identifier updates existing entry (no duplicate sources) - ForcePanic temporarily locks tier and restores gradually after duration --- ## 11 — BPC_MovementStateSystem: Movement Mode & Posture **What It Does:** The "movement oracle" — tracks the player's current state (movement mode + posture), reports changes via dispatchers, and applies movement settings to the CharacterMovementComponent. Other systems query here instead of polling the movement component directly. Bridges gameplay state to GASP animation. **How It Works Internally:** **Dual State Tracking:** - `CurrentMovementMode` (Idle, Walking, Jogging, Sprinting, CrouchWalk, Sneaking) — speed tier - `CurrentPosture` (Standing, Crouching, Prone, Sliding, Climbing, Vaulting) — body position - These are independent — you can be Sprinting while Standing, or CrouchWalk while Crouching - `PreviousMovementMode` and `PreviousPosture` stored for transition detection **Movement Settings Application:** - `MovementSettings` map (Mode → `S_MovementSettings`) defines: `MaxWalkSpeed`, `Acceleration`, `Deceleration`, `GroundFriction`, `bCanSprint`, `bCanCrouch`, `StaminaDrainMultiplier` - `SetMovementMode(NewMode)` applies the corresponding settings to `CharacterMovementComponent` - `SetPosture(NewPosture)` adjusts CapsuleComponent half-height (Standing=96, Crouch=60, Prone=30) - Prone requires going through Crouching first (enforced) **Movement Penalties:** - `ApplyMovementPenalty(Multiplier, Duration, PenaltyTag)` temporarily multiplies MaxWalkSpeed - Timer restores original value after Duration - Used by stress system (movement penalty at high stress), injury system, debuffs - Each penalty tagged for tracking — multiple penalties stack multiplicatively? **Footstep System:** - `CalculateFootstep()`: line trace downward from foot → get surface physical material → look up `S_FootstepProfile` matching surface and mode - `DefaultFootstepProfile` as fallback - Fires `OnFootstep` dispatcher with mode and surface type for audio system - Velocity threshold determines soft vs hard footstep sound **GASP Integration:** - On every mode/posture change: notify ABP_GASP via direct reference or interface - Sets GASP-specific animation variables (bStrafing, bSprinting, gait, stance) - This is the ONLY component that talks directly to ABP_GASP for movement state **Implementation Patterns:** - Does NOT handle input — input remains in `PC_PlayerController` - `CanTransitionToPosture(TargetPosture)` traces for ceiling clearance - `SetSprinting(bSprinting)` wraps SetMovementMode with stamina awareness - `OnMovementUpdated(Tick)` checks velocity, detects start/stop, tracks ground state - `GetCurrentSpeedNormalised()` for smooth animation blend values **Integration Points:** - **Called by:** `PC_PlayerController` (input → mode changes) - **Broadcasts:** `OnMovementModeChanged`, `OnPostureChanged`, `OnMovementStart/Stop`, `OnSprintStateChanged`, `OnJumped/Landed`, `OnFootstep` - **Key consumers:** `ABP_GASP` (animation), `BPC_StaminaSystem` (drain/regen), `BPC_CameraStateLayer` (FOV), `BP_AudioManager` (footsteps), `BPC_InteractionDetector` (range scaling) **Edge Cases:** - Forced transitions (knockdown) apply impulse regardless of current state - Cannot transition to upright if ceiling is too low (traced by CanTransitionToPosture) - Rapid mode switching is protected by TransitionBlendTime - Sprinting auto-stops when stamina hits 0 (external listener) --- ## 12 — BPC_HidingSystem: Stealth & Concealment **What It Does:** Manages the player entering, occupying, peeking from, and exiting environmental hiding spots. Handles line-of-sight checks against enemies, breath-holding mechanics, and stress reduction while concealed. The central hub for all stealth gameplay. **How It Works Internally:** **Hide State Machine:** ``` Exposed → Entering → Hidden ⇄ Peeking → Exiting → Exposed ``` - `Entering/Exiting`: transient animation states - `Hidden`: fully concealed, LOS blocked, stress decaying - `Peeking`: partial exposure, camera offset, timed max duration - Each transition broadcasts `OnHideStateChanged` **Hide Spot Types:** - `Locker`: fully enclosed (wardrobe, locker) — complete concealment - `BehindCover`: behind low wall/crate — peeking possible - `Under`: under bed/table — prone entry - `InShadow`: standing in shadow volume — dynamic, proximity-based - `TallGrass`: crouch-moving through vegetation — partial concealment - Each type stored in `S_HideSpotInfo` from the hiding spot actor **Entry Protocol:** 1. Player presses interact near a hide spot 2. `EnterHideSpot(HideSpotActor)` → validate I_HidingSpot interface, check slot availability 3. Set `CurrentHideState = Entering`, disable movement input 4. Play entering animation → animation notify triggers `OnAnimationHideEnterComplete` 5. Set `CurrentHideState = Hidden`, start stress decay timer, start periodic LOS check **Line of Sight (LOS) System:** - `IsPlayerDetectable(EnemyLocation, DetectionRange)` called by AI perception - If not hidden: always detectable - If hidden: line trace from enemy to player → if trace hits hide spot actor before player: concealed - Peeking state increases detection radius - `PerformLOSCheck()` runs on timer: iterates all enemies in range, checks each - If any enemy has LOS: fire `OnHideSpotCompromised` → stress spike, forced exit warning **Peek System:** - `StartPeek(Direction)` — Left/Right/Over from behind cover - Camera moves to peek socket location on hide spot actor - `MaxPeekDuration` timer forces return to Hidden - `PeekCooldown` prevents rapid toggling **Stress Interaction:** - `StressDecayWhileHidden` applied per second while in Hidden state - `bBlocksStress` on hide spot prevents new stress sources from affecting player - Hide spots double as psychological safe rooms **Implementation Patterns:** - `ForceKickFromHide()` for enemy discovery — bForceExit skips animation - `TryBreathHold()` reduces noise for 8 seconds near enemies - `OnDamageWhileHiding` handler: penetrating damage forces exit; non-penetrating converts to stress - Destroying a hide spot while player is inside force-exits them **Integration Points:** - **Called by:** `PC_PlayerController` (interact input), AI (IsPlayerDetectable queries) - **Broadcasts:** `OnHideStateChanged`, `OnEntered/ExitedHidingSpot`, `OnPeekStarted/Ended`, `OnHideSpotCompromised`, `OnForcedExitWarning`, `OnBreathHoldChanged` - **Key consumers:** `BPC_CameraStateLayer` (peek/hide offsets), `BPC_StressSystem` (decay), `ABP_GASP` (hide anims), `AI_EnemyController` (awareness), `WBP_HUD` (hide indicators) **Edge Cases:** - EnterHideSpot while already hiding returns false - ExitHideSpot while Exposed does nothing - Multiple players can't occupy limited-slot spots - Peek max duration and cooldown enforced - Shutting down the hide spot actor force-exits the player --- ## 13 — BPC_EmbodimentSystem: First-Person Body **What It Does:** Creates first-person body awareness — the player sees their arms, shadow, and optionally legs/torso in context-appropriate situations. Manages mesh visibility modes, environmental body overlays (blood, water, mud), wall proximity detection for arm IK, and shadow casting. **How It Works Internally:** **Visibility Modes:** - `FullBody` (third-person, mirrors, death) → `ArmsOnly` (default first-person) → `ArmsAndShadow` → `Minimal` (only hands, high stress) → `Hidden` (cutscenes, UI) - `SetVisibilityMode(NewMode, bInstant)` toggles mesh components: ArmsMesh, BodyShadowComponent, optional TorsoMesh - `S_BodyPartVisibility` struct derived from mode controls individual part visibility **Overlay System:** - `E_BodyOverlayState`: Clean, BloodSpatter, WaterDroplets, Mud, Toxic - `ApplyOverlay(Overlay)` with blend: sets MaterialParameter on ArmsMesh toward `TargetValue` at `BlendSpeed` - Auto-fades after `Duration` seconds - `ClearOverlay(Type)` removes single or all overlays - Multiple overlays stack: blood + water = combined effect on material - `ApplyOverlayBlendTick()` runs each frame for smooth transitions **Wall Proximity / Arm IK:** - `CheckWallProximity()`: line trace from camera forward by `BrushTraceDistance` - If wall detected: apply IK offset to arms (avoids clipping), set `bIsNearWall = true` - Fires `OnWallProximityChanged` → ABP_Arms for IK adjustment **Damage/Environment Integration:** - `OnDamageTakenBloodHandler`: blood overlay intensity scales with damage amount - `OnEnterWaterHandler`: water droplets overlay with fast blend, auto-dries - Bound to `BPC_HealthSystem.OnDamageTaken` and water volume triggers **Implementation Patterns:** - Mesh references cached at BeginPlay from owner (first-person skeletal mesh) - Shadow uses separate component with shadow-only material — not full character mesh - All overlay blending uses Material Parameter Collections for real-time updates - Wall proximity is simple line trace; advanced IK requires arms animation blueprint **Integration Points:** - **Listens to:** `BPC_HealthSystem.OnDamageTaken` (blood), water volume triggers - **Broadcasts:** `OnVisibilityModeChanged` → `BPC_CameraStateLayer` (FOV adjust), `OnOverlayChanged` → `WBP_HUD` (screen effects), `OnWallProximityChanged` → ABP_Arms **Edge Cases:** - Rapid visibility changes don't cause rendering glitches due to fade transitions - Overlays stack: intensity values combined via material logic - Hidden mode overrides all — hides everything regardless of other settings --- ## 14 — BPC_CameraStateLayer: Dynamic Camera Controller **What It Does:** Centralizes all camera-modifying logic: field-of-view changes per state, camera shake system with priority queuing, post-process overrides (vignette, chromatic aberration, color grading), head bob, and location/rotation offsets per game state. Other systems request camera changes here instead of directly manipulating the camera. **How It Works Internally:** **Camera State System:** - 10 states: Default, Aiming, Sprinting, Crouching, Peeking, Hiding, Stressed, Injured, Death, Cutscene - Each has `S_CameraStateConfig`: TargetFOV, BlendSpeed, HeadOffset, HeadRotation, pitch constraints, head bob settings - `RequestCameraState(NewState, bImmediate)` sets targets and starts blending - `BlendToTargetFOV()` runs per tick: smooth interpolation to target FOV - `BlendCameraOffset()` interpolates head position/rotation offsets **Camera Shake System:** - `S_CameraShakeRequest`: ShakeClass, Scale, Priority (Low/Medium/High/Cinematic), Duration, bIsLooping, Tag - Priority system: higher priority shakes override lower; same tag replaces existing - `PlayCameraShake(ShakeRequest)` adds to ActiveShakeRequests map and starts playback - If Duration > 0: timer auto-stops shake - `StopCameraShake(ShakeTag)` removes specific shake **Post-Process Overrides:** - `S_PostProcessOverride`: vignette, chromatic aberration, color grading LUT - `ApplyPostProcessOverride(Override, bInstant)` blends Material Parameter Collection values - `ClearPostProcessOverride(BlendTime)` returns to defaults - Used by stress system (CA at high stress), injury (vignette), death (fade to black) **Dynamic Effects:** - **Sprint:** FOV widens by SprintFOVMultiplier - **Stress:** `UpdateStressPulse()` applies sine-wave FOV oscillation for "unease" feel - **Crouch:** slight downward offset - **Peek:** camera moves to hide spot peek socket - **Hiding:** constricted FOV, darkness effect **Reactivity:** - Binds to `BPC_MovementStateSystem.OnMovementModeChanged` → Sprint/Crouch/Default FOV - Binds to `BPC_StressSystem.OnStressTierChanged` → stress effects at Panicked+ - Binds to `BPC_HealthSystem.OnDamageTaken` → injury shake, vignette - Called directly by `BPC_HidingSystem` for peek/hide camera states **Implementation Patterns:** - Uses Material Parameter Collection (MPC_CameraEffects) for post-process parameters - Camera shakes authored as Blueprint Camera Shake classes - Pitch constraints enforced during hiding/peeking states - All blending is frame-rate independent via DeltaTime **Integration Points:** - **Broadcasts:** `OnCameraStateChanged`, `OnFOVChanged`, `OnShakeStarted/Ended`, `OnPostProcessChanged` - **Called by:** Any system needing camera change - **Key consumers:** PlayerCameraManager (direct application), `WBP_HUD` (post-process materials) **Edge Cases:** - Shake with same tag and lower priority is ignored (not stacked) - Rapid FOV switching blends smoothly — no snap transitions - Stress pulse uses sine wave to prevent jitter - All active shakes stopped on component destroy or pawn unpossess --- ## 15 — BPC_PlayerMetricsTracker: Analytics & Playstyle **What It Does:** Records player behavior metrics throughout a session: accuracy (shots fired vs hit), deaths count, damage dealt/received, items used, hiding time, sprint time, objectives completed. Used by `BPC_PlaystyleClassifier` to categorize player as Aggressive/Stealthy/Explorer/Balanced, and by `BPC_DifficultyManager` for adaptive tuning. **How It Works Internally:** - Listens to relevant dispatchers from HealthSystem (deaths), WeaponBase (shots/hits), InventorySystem (items used), HidingSystem (hide time), MovementState (sprint time) - Accumulates counters and ratios internally - Provides query functions for other adaptive systems - Persists across sessions via `I_Persistable` for long-term playstyle tracking **Implementation Patterns:** - Pure listener — never calls into other systems, only receives events - All metrics are gameplay-tag keyed for extensibility - Ratios computed on demand, not continuously --- ## Common Implementation Patterns in This Category 1. **Cascade Architecture:** Systems don't poll each other — they bind to dispatchers. Health change → stress reaction → camera effect → movement penalty. Each system reacts independently. 2. **Normalized Values for UI:** `GetHealthNormalised()`, `GetStaminaNormalised()`, `GetStressNormalised()` all return 0.0-1.0 — UI widgets interpolate smoothly. 3. **Timer-Based Ticks:** Regen, drain, stress decay all use 0.1s interval timers — not event tick. Better performance. 4. **State Not Polling:** Camera doesn't ask "am I sprinting?" — it binds to `OnMovementModeChanged` and reacts. 5. **Threshold Not Continuous:** Stress/hiding systems use tier/state thresholds — other systems react to tier changes, not raw values. 6. **Data Config Everything:** Damage resistances, movement speeds, stamina drain rates, stress thresholds — all in exposed variables or Data Assets, never hardcoded. --- ## Multiplayer Networking ### Category Authority Map | System | Authority Pattern | Client Prediction | |--------|------------------|-------------------| | `BPC_HealthSystem` | Server-authoritative damage/healing/death | Client predicts damage flash; server corrects health via OnRep | | `BPC_StaminaSystem` | Server-authoritative drain/restore | Client predicts bar decrease; server corrects via OnRep | | `BPC_StressSystem` | Server-authoritative stress accumulation | Client plays local hallucinations; server is canonical | | `BPC_MovementStateSystem` | Server-authoritative mode/posture | Native CMC replication handles position/velocity | | `BPC_HidingSystem` | Server-authoritative enter/exit/peek | Client predicts animation; server validates slot availability | | `BPC_EmbodimentSystem` | Minimal — visibility/overlay state only synced | Rendering is local per-client | | `BPC_CameraStateLayer` | State enum only synced | FOV, shake, post-process are local per-client | | `BPC_PlayerMetricsTracker` | Server accumulates; snapshot replicates | No client prediction needed | ### Key Server RPCs - `Server_ApplyDamage` / `Server_ApplyHealing` — validated by server before health modification - `Server_DrainStamina` / `Server_StartContinuousDrain` — validated against MinStamina thresholds - `Server_AddStress` / `Server_AddStressSource` — stress sources validated server-side - `Server_SetMovementMode` / `Server_SetPosture` — validated against MovementSettings - `Server_EnterHideSpot` / `Server_ExitHideSpot` — validated against slot availability ### Multiplayer Cascade The cascade (Health→Stress→Camera→Movement) works identically in multiplayer because each system fires the same dispatchers regardless of whether the state change came from local mutation or `OnRep`. No special-case multiplayer code needed in the cascade chain. ### Full Spec See [`docs/architecture/multiplayer-networking.md`](../architecture/multiplayer-networking.md) Section 3.2. --- *Developer Reference v1.0 — 02 Player Systems. Companion to docs/blueprints/02-player/ specs.*