# Player Character — BP_HorrorPlayerCharacter **Game:** Project Void | **Asset:** `BP_HorrorPlayerCharacter` | **Parent:** GASP-based Character **Asset Path:** `Content/Game/Characters/BP_HorrorPlayerCharacter.uasset` **Build Phase:** 4 | **Demonstrates:** All 02-player systems (08-15) + 03-interaction (16) + 04-inventory (26,28,30,31,34) + 08-weapons (70,71,72) + 16-state (130) --- ## Purpose The player's physical embodiment in the game world. A GASP-based first-person character with all framework components attached. This is the single most important game asset — nearly every framework system interacts with it. --- ## Architecture Overview ``` BP_HorrorPlayerCharacter (Character with GASP Animation Blueprint) ├── Components (auto-attached on spawn) │ ├── CapsuleComponent (inherited) │ ├── CameraComponent (first-person, head socket) │ ├── CharacterMovementComponent (GASP — read-only) │ ├── SkeletalMeshComponent (GASP — read-only) │ │ │ ├── [02-player] BPC_HealthSystem # 08 — Health, damage, death │ ├── [02-player] BPC_StaminaSystem # 09 — Sprint, action drain │ ├── [02-player] BPC_StressSystem # 10 — Psychological stress │ ├── [02-player] BPC_MovementStateSystem # 11 — Walk/Sprint/Crouch │ ├── [02-player] BPC_HidingSystem # 12 — Hide in lockers/beds │ ├── [02-player] BPC_EmbodimentSystem # 13 — First-person body │ ├── [02-player] BPC_CameraStateLayer # 14 — FOV/offset layers │ ├── [02-player] BPC_PlayerMetricsTracker # 15 — Accuracy, style │ │ │ ├── [03-interaction] BPC_InteractionDetector # 16 — Raycast interact │ │ │ ├── [04-inventory] BPC_InventorySystem # 31 — Inventory grid │ ├── [04-inventory] BPC_EquipmentSlotSystem # 30 — Weapon/tool slots │ ├── [04-inventory] BPC_ActiveItemSystem # 26 — Quick-slot cycling │ ├── [04-inventory] BPC_ConsumableSystem # 28 — Use medkits/etc │ ├── [04-inventory] BPC_KeyItemSystem # 34 — Key items │ ├── [04-inventory] BPC_JournalSystem # 33 — Objectives │ ├── [04-inventory] BPC_CollectibleTracker # 27 — Collectibles found │ ├── [04-inventory] BPC_DocumentArchiveSystem# 29 — Read documents │ ├── [04-inventory] BPC_ItemCombineSystem # 32 — Combine items │ │ │ ├── [08-weapons] BPC_AmmoComponent # 70 — Ammo pool │ ├── [08-weapons] BPC_RecoilSystem # 77 — Weapon recoil │ ├── [08-weapons] BPC_HitReactionSystem # 75 — Hit flinch │ ├── [08-weapons] BPC_DamageReceptionSystem # 72 — Receive damage │ ├── [08-weapons] BPC_CombatFeedbackComponent# 71 — Hit markers │ │ │ ├── [16-state] BPC_StateManager # 130 — Action gating │ │ │ ├── [07-narrative] BPC_NarrativeStateSystem # 58 — Narrative flags │ ├── [07-narrative] BPC_ObjectiveSystem # 59 — Active quests │ └── [07-narrative] BPC_EndingAccumulator # 68 — Ending tracker │ ├── Interfaces Implemented │ ├── I_Damageable (take damage) │ ├── I_Interactable (can be interacted with by NPCs) │ └── I_Persistable (save/load state) ``` --- ## Creation Steps ### Step 1 — Create Blueprint ``` Content Browser → Game/Characters/ Right-click → Blueprint Class Parent Class: Your GASP Character (or Character if GASP is added to ABP only) Name: BP_HorrorPlayerCharacter ``` ### Step 2 — Add All Components In the Components panel, add each component from the list above. Order doesn't matter for Blueprint components — they auto-initialize on BeginPlay. **Tip:** Create a Blueprint function `InitializeAllComponents` called from `Event BeginPlay` and verify every component is valid before proceeding. ### Step 3 — Configure GASP Integration ``` [GASP Animation Blueprint Setup] │ ├─ Assign GASP AnimBP to SkeletalMeshComponent → Anim Class │ └─ Default: ABP_GASP (read-only, do not modify) │ ├─ [GASP Notifies] (extend via Animation Notify overrides, NOT by editing GASP) │ └─ Add AnimNotify slots to GASP montages in your ABP child: │ ├─ Notify_Footstep → BPC_MovementStateSystem.OnFootstep() │ ├─ Notify_EnterAction → BPC_StateManager.EnterAction(Tag) │ ├─ Notify_ExitAction → BPC_StateManager.ExitAction() │ ├─ Notify_DamageApplied → BPC_DamageReceptionSystem.ReceiveDamage() │ └─ (etc — see animation-catalog.md for all 14 notifies) │ └─ [Motion Matching Database] └─ Assign your motion matching pose search database (GASP handles locomotion blend — you provide the poses) ``` ### Step 4 — Wire Event BeginPlay ``` Event BeginPlay │ ├─ Parent: Event BeginPlay (essential for GASP init) │ ├─ [Validate All Components] │ ├─ For each BPC_ component: │ │ ├─ GetComponentByClass → IsValid? → Log │ │ └─ If invalid: FL_GameUtilities.LogError("Missing component: X") │ │ │ └─ REQUIRED components (game cannot function without): │ ├─ BPC_HealthSystem → if missing, ERROR + disable input │ ├─ BPC_InventorySystem → if missing, ERROR │ ├─ BPC_StateManager → if missing, ERROR │ └─ BPC_InteractionDetector → if missing, ERROR │ ├─ [Initialize Health] │ ├─ BPC_HealthSystem.SetMaxHealth(100.0) │ └─ BPC_HealthSystem.SetCurrentHealth(100.0) │ ├─ [Initialize Stamina] │ ├─ BPC_StaminaSystem.SetMaxStamina(100.0) │ └─ BPC_StaminaSystem.SetCurrentStamina(100.0) │ ├─ [Initialize Stress] │ ├─ BPC_StressSystem.SetCurrentStress(0.0) │ └─ BPC_StressSystem.SetStressDecayRate(1.0) // per second in safe areas │ ├─ [Initialize Movement] │ ├─ BPC_MovementStateSystem.SetMovementMode(Walking) │ └─ BPC_MovementStateSystem.SetPosture(Standing) │ ├─ [Initialize Camera] │ ├─ BPC_CameraStateLayer.SetDefaultFOV(90.0) │ └─ BPC_EmbodimentSystem.SetVisibilityMode(FirstPerson) │ ├─ [Initialize Inventory] │ ├─ BPC_InventorySystem.Initialize(GridWidth=6, GridHeight=4, MaxWeight=50.0) │ └─ BPC_EquipmentSlotSystem.Initialize() │ └─ Create slots: PrimaryWeapon, Tool, Armor (3 equipment slots) │ ├─ [Initialize State Manager] │ ├─ BPC_StateManager.SetupGatingRules(DA_StateGatingTable) │ └─ BPC_StateManager.SetInitialState(Standing) │ ├─ [Bind System Interconnections] │ │ │ ├─ BPC_HealthSystem.OnHealthChanged → BPC_StateManager.UpdateVitalSignals │ ├─ BPC_HealthSystem.OnDeath → BPC_DeathHandlingSystem.HandleDeath │ ├─ BPC_StressSystem.OnStressTierChanged → BPC_CameraStateLayer.SetStressBlur │ ├─ BPC_StressSystem.OnStressTierChanged → SS_AudioManager.SetFloatParameter("Stress", value) │ ├─ BPC_StaminaSystem.OnStaminaDepleted → BPC_StateManager.ForcePushState(Exhausted) │ ├─ BPC_MovementStateSystem.OnModeChanged → BPC_StateManager.UpdateMovementState │ ├─ BPC_MovementStateSystem.OnFootstep → SS_AudioManager.PlayFootstep(SurfaceTag) │ ├─ BPC_HidingSystem.OnHideStateChanged → BPC_StateManager.SetOverlayState │ ├─ BPC_HidingSystem.OnHideStateChanged → SS_EnhancedInputManager.PushContext(Hiding) │ └─ (etc — any cross-system event binding) │ └─ [Ready] └─ Broadcast OnPlayerReady └─ Other systems wait for this before querying player state ``` ### Step 5 — Wire Input Handling ``` [Event Graph — Input] IA_Move (Axis2D) → CharacterMovementComponent.AddMovementInput IA_Look (Axis2D) → AddControllerYawInput + AddControllerPitchInput │ ├─ [State Gating Check] │ └─ BPC_StateManager.IsActionPermitted(Move) ? │ ├─ True → process input │ └─ False → ignore input (blocked by state: cutscene, death, etc.) │ IA_Interact (Pressed) → BPC_InteractionDetector.Interact() └─ Also check: BPC_StateManager.IsActionPermitted(Interact) IA_Sprint (Pressed) → BPC_StaminaSystem.StartSprint() └─ BPC_StateManager.IsActionPermitted(Sprint)? AND Stamina > 0? IA_Sprint (Released) → BPC_StaminaSystem.StopSprint() IA_Crouch (Pressed) → BPC_MovementStateSystem.ToggleCrouch() └─ BPC_StateManager.IsActionPermitted(Crouch)? IA_Fire (Pressed) → BPC_ActiveItemSystem.UseEquippedItem() ├─ Routes to BP_Pistol_Held.UseItem() or BP_Shotgun_Held.UseItem() └─ BPC_StateManager.IsActionPermitted(Fire)? IA_Fire (Released) → (cease fire for auto weapons) IA_Aim (Pressed) → BPC_CameraStateLayer.SetLayer(Aiming) └─ FOV zooms to 55, slight camera offset forward IA_Aim (Released) → BPC_CameraStateLayer.RemoveLayer(Aiming) └─ FOV returns to 90 IA_Reload (Pressed) → BPC_ActiveItemSystem.ReloadEquippedItem() └─ Routes to BP_Pistol_Held.Reload() or BP_Shotgun_Held.Reload() IA_UseItem (Pressed) → BPC_ConsumableSystem.UseQuickSlotItem() └─ Uses whatever is in the quick-use slot (medkit, syringe, etc.) IA_Flashlight (Pressed) → Toggle flashlight (if equipped in Tool slot) └─ Calls I_Toggleable.Toggle on BP_Flashlight_Held IA_Jump (Pressed) → Character.Jump() └─ Also: BPC_ContextualTraversalSystem.TryVaultOrMantle() IA_OpenWatch (Pressed) → SS_EnhancedInputManager.PushContext(WristwatchUI) ├─ SS_UIManager.ShowMenu(InventoryMenu) └─ Camera pans down to wristwatch IA_PauseMenu (Pressed) → PC_HorrorController.OpenPauseMenu() IA_QuickHeal (Pressed) → BPC_ConsumableSystem.QuickUse(MedKit) └─ Uses first available medkit in inventory IA_QuickSlot1-8 → BPC_ActiveItemSystem.SetQuickSlot(SlotIndex) ``` ### Step 6 — Wire Component Communication Each BPC_ component should communicate via **Event Dispatchers**, not direct references to other components. This keeps components decoupled. ``` Core Dispatcher Bindings (in Event BeginPlay): [Damage Pipeline] BPC_DamageReceptionSystem.OnDamageReceived ├─ → BPC_HealthSystem.ApplyDamage(Damage) ├─ → BPC_HitReactionSystem.PlayHitReaction(DamageInfo) ├─ → BPC_CombatFeedbackComponent.ShowHitMarker(DamageInfo) ├─ → WBP_ScreenEffectController.ShowDamageVignette() └─ → BPC_PlayerMetricsTracker.RecordDamageTaken(Damage) [Health to State] BPC_HealthSystem.OnHealthChanged(NewHealth, MaxHealth) ├─ → BPC_StateManager.UpdateVitalSignals() ├─ → WBP_DiegeticHUDFrame.UpdateHealthBar() └─ → SS_AudioManager.SetFloatParameter("HealthPercent", ratio) [Health to Death] BPC_HealthSystem.OnDeath(DeathCause, Killer) ├─ → BPC_DeathHandlingSystem.HandleDeath() └─ → BPC_StateManager.ForcePushState(Death) [Stress System] BPC_StressSystem.OnStressTierChanged(OldTier, NewTier) ├─ → PS_HorrorPlayerState.SetSanityTier(NewTier * 25) // convert tier to 0-100 ├─ → BPC_CameraStateLayer.SetStressBlur(NewTier) │ ├─ Calm → no blur │ ├─ Uneasy → slight peripheral blur │ ├─ Disturbed → moderate blur + vignette │ ├─ Breaking → heavy blur + tunnel vision │ └─ Catatonic → near-black screen ├─ → BPC_MemoryDriftSystem.SetIntensity(NewTier) │ └─ Triggers visual/audio hallucinations ├─ → SS_AudioManager.SetFloatParameter("StressTier", NewTier) └─ → BPC_StateManager.UpdateVitalSignals() [Stamina System] BPC_StaminaSystem.OnStaminaChanged(Current, Max) ├─ → WBP_DiegeticHUDFrame.UpdateStaminaBar() └─ → (If depleted) BPC_StateManager.ForcePushState(Exhausted) └─ Blocks Sprint action for 3 seconds [Hiding System] BPC_HidingSystem.OnEnterHiding(HidingSpot) ├─ → BPC_StateManager.SetOverlayState(Hiding) ├─ → SS_EnhancedInputManager.PushContext(Hiding) ├─ → BPC_CameraStateLayer.SetLayer(HidePeek) ├─ → BPC_StressSystem.StartStressDecay() // stress decays while hidden └─ → WBP_DiegeticHUDFrame.ShowBreathHoldMeter() BPC_HidingSystem.OnExitHiding() ├─ → BPC_StateManager.ClearOverlayState(Hiding) ├─ → SS_EnhancedInputManager.PopContext(Hiding) ├─ → BPC_CameraStateLayer.RemoveLayer(HidePeek) └─ → WBP_DiegeticHUDFrame.HideBreathHoldMeter() [Inventory Events] BPC_InventorySystem.OnItemAdded(ItemData, SlotIndex) ├─ → WBP_InventoryMenu.UpdateSlot(SlotIndex) ├─ → BPC_CollectibleTracker.CheckItem(ItemData) → if collectible, track it ├─ → WBP_NotificationToast.Show("Picked up: " + DisplayName) └─ → (If weapon) BPC_EquipmentSlotSystem.AutoEquipIfSlotEmpty(ItemData) BPC_InventorySystem.OnItemRemoved(ItemData, SlotIndex) ├─ → WBP_InventoryMenu.ClearSlot(SlotIndex) └─ → (If equipped) BPC_EquipmentSlotSystem.UnequipSlot() [Equipment Events] BPC_EquipmentSlotSystem.OnItemEquipped(SlotTag, ItemData) ├─ → BPC_StateManager.SetEquipmentState(SlotTag, ItemData) ├─ → Spawn BP_*_Held actor (pistol, shotgun, flashlight) │ └─ Attach to hand socket on SkeletalMeshComponent └─ → BPC_ActiveItemSystem.SetCurrentItem(SlotTag, HeldActor) [State Manager Events] BPC_StateManager.OnStateChanged(OldState, NewState) ├─ → BPC_MovementStateSystem.SetStateRestrictions(NewState) │ └─ Death → no movement; Cutscene → no movement; etc. ├─ → WBP_HUDController.UpdateHUDActive(newState) │ └─ Hide HUD during cutscene/death; show during gameplay └─ → SS_EnhancedInputManager.SetContextVisibility(NewState) └─ Disable gameplay contexts during UI/Cutscene states BPC_StateManager.OnActionDenied(ActionTag, BlockReason) └─ → WBP_InteractionPromptDisplay.ShowBlocked(ActionTag, BlockReason) └─ "Cannot fire — you are hiding" └─ "Cannot interact — cutscene playing" ``` --- ## Health, Stamina, Stress — The Core Survival Triad These three systems work together to create the horror survival experience: ``` ┌──────────────────────┐ │ HEALTH (100 max) │ │ ├─ Damage reduces │ │ ├─ Medkit restores │ │ └─ 0 HP = DEATH │ └──────────┬───────────┘ │ low health ▼ ┌──────────────────────┐ ┌──────────────────────┐ │ STAMINA (100 max) │ │ STRESS (0-100) │ │ ├─ Sprint: -10/sec │ │ ├─ Darkness: +2/sec │ │ ├─ Vault: -15/use │ │ ├─ Enemy near: +5/s │ │ ├─ Combat: -5/action │ │ ├─ Void: +8/sec │ │ └─ Regen: +5/sec │ │ └─ Decay: -1/sec │ │ (only when still)│ │ (safe areas) │ └──────────┬───────────┘ └──────────┬───────────┘ │ depleted │ high stress (75+) ▼ ▼ ┌──────────────┐ ┌──────────────────┐ │ EXHAUSTED │ │ HALLUCINATIONS │ │ ├─ Can't run│ │ ├─ False enemies │ │ ├─ Walk slow│ │ ├─ Sounds/lights │ │ └─ 3s cooldown│ │ └─ Screen distort│ └──────────────┘ └──────────────────┘ ``` --- ## Key Variables (Class Defaults) | Variable | Type | Default | Purpose | |----------|------|---------|---------| | `MaxHealth` | Float | `100.0` | Set via BPC_HealthSystem in BeginPlay | | `MaxStamina` | Float | `100.0` | Set via BPC_StaminaSystem | | `MaxStress` | Float | `100.0` | Set via BPC_StressSystem | | `WalkSpeed` | Float | `300.0` | GASP default | | `SprintSpeed` | Float | `600.0` | GASP default | | `CrouchSpeed` | Float | `150.0` | GASP default | | `InteractionRange` | Float | `300.0` | How far the player can interact | | `bCanSprint` | Boolean | `true` | Disabled when exhausted or state-blocked | | `bIsFirstPerson` | Boolean | `true` | Horror game — no third-person toggle | | `DefaultFOV` | Float | `90.0` | Normal gameplay FOV | | `AimFOV` | Float | `55.0` | When aiming down sights | | `HidePeekFOV` | Float | `70.0` | When peeking from hiding | --- ## Blueprint Wiring Checklist ### Components - [ ] Add BPC_HealthSystem (08) - [ ] Add BPC_StaminaSystem (09) - [ ] Add BPC_StressSystem (10) - [ ] Add BPC_MovementStateSystem (11) - [ ] Add BPC_HidingSystem (12) - [ ] Add BPC_EmbodimentSystem (13) - [ ] Add BPC_CameraStateLayer (14) - [ ] Add BPC_PlayerMetricsTracker (15) - [ ] Add BPC_InteractionDetector (16) - [ ] Add BPC_InventorySystem (31) - [ ] Add BPC_EquipmentSlotSystem (30) - [ ] Add BPC_ActiveItemSystem (26) - [ ] Add BPC_ConsumableSystem (28) - [ ] Add BPC_KeyItemSystem (34) - [ ] Add BPC_JournalSystem (33) - [ ] Add BPC_CollectibleTracker (27) - [ ] Add BPC_DocumentArchiveSystem (29) - [ ] Add BPC_ItemCombineSystem (32) - [ ] Add BPC_AmmoComponent (70) - [ ] Add BPC_RecoilSystem (77) - [ ] Add BPC_HitReactionSystem (75) - [ ] Add BPC_DamageReceptionSystem (72) - [ ] Add BPC_CombatFeedbackComponent (71) - [ ] Add BPC_StateManager (130) - [ ] Add BPC_NarrativeStateSystem (58) - [ ] Add BPC_ObjectiveSystem (59) - [ ] Add BPC_EndingAccumulator (68) ### Event BeginPlay - [ ] Call parent BeginPlay - [ ] Validate all critical components → log errors if missing - [ ] Initialize Health, Stamina, Stress to max values - [ ] Initialize Inventory (6×4 grid, 50 weight) - [ ] Initialize Equipment Slots (PrimaryWeapon, Tool, Armor) - [ ] Initialize State Manager with DA_StateGatingTable - [ ] Bind all inter-component dispatchers (Health→Death, Stress→Blur, etc.) ### Input - [ ] Bind IA_Move, IA_Look (always active unless state-blocked) - [ ] Bind IA_Interact (state-gated) - [ ] Bind IA_Sprint (stamina-gated + state-gated) - [ ] Bind IA_Crouch (state-gated) - [ ] Bind IA_Fire (state-gated + equipment-gated) - [ ] Bind IA_Aim (equipment-gated) - [ ] Bind IA_Reload (state-gated) - [ ] Bind IA_UseItem (state-gated) - [ ] Bind IA_Flashlight (state-gated) - [ ] Bind IA_Jump (state-gated) - [ ] Bind IA_OpenWatch (state-gated → pushes wristwatch context) - [ ] Bind IA_PauseMenu - [ ] Bind IA_QuickHeal - [ ] Bind IA_QuickSlot1-8 - [ ] Every input check: BPC_StateManager.IsActionPermitted before processing --- ## Notes for Expansion - **GASP customization:** Never edit GASP AnimBP directly. Create a child AnimBP and override/insert notifies. Add your own locomotion states (limping, crawling) as new GASP states. - **Component order:** Add components in dependency order if they have BeginPlay initialization dependencies. Health before DeathHandling; Inventory before Equipment. - **Multiplayer:** All health/stamina/stress mutations must be server-authoritative. Use `HasAuthority()` gates. Replicate with `RepNotify`. - **Performance:** If FPS drops, consider lazy-initializing non-critical components (CollectibleTracker, DocumentArchive, PlayerMetricsTracker). - **Mod support:** Keep all default values in a `DA_PlayerConfig` Data Asset (health, stamina, speed, FOV) so modders can override without touching BP. --- *BP_HorrorPlayerCharacter — The player pawn for Project Void. See [GAMEINDEX.md](GAMEINDEX.md) for full game structure. See [INDEX.md](../blueprints/INDEX.md) for all framework component specs.*