From 040db37720fca20147dedcebe26b2f0b469b8128 Mon Sep 17 00:00:00 2001 From: Lefteris Notas Date: Thu, 21 May 2026 22:27:57 +0300 Subject: [PATCH] Add UI Overrides and Weapons Index documentation for Project Void - Created ui-overrides.md detailing game-specific Widget Blueprint overrides, including purpose, widget index, visual styling, and accessibility requirements. - Established weapons-index.md outlining all held weapon actors, including their components, logic, and comparisons for gameplay mechanics. --- docs/game/GAMEINDEX.md | 706 ++++++++++++++++++++++++++++ docs/game/README.md | 216 +++++++-- docs/game/atmosphere-audio.md | 367 +++++++++++++++ docs/game/core-controller-state.md | 308 ++++++++++++ docs/game/core-gameinstance.md | 188 ++++++++ docs/game/core-gamemode.md | 275 +++++++++++ docs/game/enemies-index.md | 371 +++++++++++++++ docs/game/items-index.md | 360 ++++++++++++++ docs/game/levels.md | 642 +++++++++++++++++++++++++ docs/game/narrative-progression.md | 354 ++++++++++++++ docs/game/playercharacter.md | 445 ++++++++++++++++++ docs/game/polish-loading-credits.md | 354 ++++++++++++++ docs/game/save-checkpoints.md | 387 +++++++++++++++ docs/game/scene-flow.md | 558 ++++++++++++++++++++++ docs/game/state-gating-examples.md | 287 +++++++++++ docs/game/ui-overrides.md | 704 +++++++++++++++++++++++++++ docs/game/weapons-index.md | 316 +++++++++++++ 17 files changed, 6795 insertions(+), 43 deletions(-) create mode 100644 docs/game/GAMEINDEX.md create mode 100644 docs/game/atmosphere-audio.md create mode 100644 docs/game/core-controller-state.md create mode 100644 docs/game/core-gameinstance.md create mode 100644 docs/game/core-gamemode.md create mode 100644 docs/game/enemies-index.md create mode 100644 docs/game/items-index.md create mode 100644 docs/game/levels.md create mode 100644 docs/game/narrative-progression.md create mode 100644 docs/game/playercharacter.md create mode 100644 docs/game/polish-loading-credits.md create mode 100644 docs/game/save-checkpoints.md create mode 100644 docs/game/scene-flow.md create mode 100644 docs/game/state-gating-examples.md create mode 100644 docs/game/ui-overrides.md create mode 100644 docs/game/weapons-index.md diff --git a/docs/game/GAMEINDEX.md b/docs/game/GAMEINDEX.md new file mode 100644 index 0000000..b58cf92 --- /dev/null +++ b/docs/game/GAMEINDEX.md @@ -0,0 +1,706 @@ +# GAMEINDEX — Project Void: Psychological Horror FPS Prototype + +**Version:** 1.0 | **Generated:** 2026-05-21 | **Target UE:** 5.5–5.7 +**Framework:** UE5 Modular Game Framework (135 systems) +**Game:** Project Void — Abandoned Asylum Psychological Horror + +--- + +## Purpose + +This document is the master index for the **"Project Void"** example game prototype. It defines: +1. The complete `Content/Game/` folder structure — every asset to create +2. Which framework system each game asset demonstrates +3. The full scene loading flow from boot to credits +4. Expansion notes for extending the prototype into a full game + +**Rule:** All game content lives in `Content/Game/`. Never modify `Content/Framework/`. Game assets reference framework systems via interfaces, dispatchers, and Data Asset children. + +--- + +## Game Concept — "Project Void" + +``` +┌─────────────────────────────────────────────────────────┐ +│ PROJECT VOID — Psychological Horror FPS │ +│ │ +│ Setting: Abandoned Blackwood Asylum, 1920s │ +│ Player: Dr. Eliza Vance — investigative journalist │ +│ Premise: You wake inside the asylum with no memory │ +│ of how you arrived. The building shifts │ +│ around you. Patients roam the halls. │ +│ Something older than the asylum │ +│ watches from the void between rooms. │ +│ │ +│ Tone: Silent Hill meets Amnesia meets Outlast │ +│ Core Loop: Explore → Find clues → Solve puzzles → │ +│ Evade threats → Piece together memory → │ +│ Unlock new areas → Reality shifts │ +└─────────────────────────────────────────────────────────┘ +``` + +--- + +## Folder Structure + +``` +Content/Game/ ← ALL game content (NEVER modify Framework/) +│ +├── GAMEINDEX.uasset ← (optional) Empty Data Asset — marker file +│ +├── Core/ ← Core game classes (child of Framework classes) +│ ├── GI_HorrorGame.uasset ← Child of GI_GameFramework +│ ├── GM_HorrorGameMode.uasset ← Child of GM_CoreGameMode +│ ├── PC_HorrorController.uasset ← Child of PC_CoreController (or PlayerController) +│ ├── PS_HorrorPlayerState.uasset ← Child of PS_CorePlayerState +│ ├── GS_HorrorGameState.uasset ← Child of GS_CoreGameState +│ └── FL_HorrorUtilities.uasset ← Child of FL_GameUtilities (game-specific utils) +│ +├── Characters/ ← Player & NPC Blueprints +│ ├── BP_HorrorPlayerCharacter.uasset ← GASP-based player pawn (all BPC_ components) +│ ├── BP_Enemy_Patient.uasset ← Melee-chase enemy (slow, persistent) +│ ├── BP_Enemy_Shade.uasset ← Teleporting horror (rare, terrifying) +│ ├── BP_Enemy_Orderly.uasset ← Fast, flashlight-vulnerable enemy +│ └── BP_NPC_Survivor.uasset ← Friendly NPC for dialogue/objectives +│ +├── Items/ ← DA_ItemData children — all game items +│ ├── DA_Item_Flashlight.uasset ← Tool — toggleable light (see item-flashlight.md) +│ ├── DA_Item_Pistol.uasset ← Weapon — 9mm (see item-pistol.md) +│ ├── DA_Item_MedKit.uasset ← Consumable — health restore (see item-medkit.md) +│ ├── DA_Item_Keycard_Omega.uasset ← KeyItem — progression gate (see item-keycard.md) +│ │ +│ ├── DA_Item_Ammo_9mm.uasset ← Ammo — pistol rounds +│ ├── DA_Item_Shotgun.uasset ← Weapon — double barrel +│ ├── DA_Item_Ammo_Shells.uasset ← Ammo — shotgun shells +│ ├── DA_Item_Battery.uasset ← Consumable — flashlight power +│ ├── DA_Item_AdrenalineSyringe.uasset ← Consumable — temporary stamina boost +│ ├── DA_Item_SanityPill.uasset ← Consumable — stress reduction +│ ├── DA_Item_Crowbar.uasset ← Tool — pry doors, melee weapon +│ ├── DA_Item_Keycard_Alpha.uasset ← KeyItem — east wing access +│ ├── DA_Item_Keycard_Beta.uasset ← KeyItem — basement access +│ ├── DA_Item_Key_Wardens.uasset ← KeyItem — warden's office +│ ├── DA_Item_JournalPage.uasset ← Document — lore collectible +│ ├── DA_Item_PatientFile.uasset ← Document — story collectible +│ ├── DA_Item_OldPhoto.uasset ← Collectible — memory fragment +│ └── DA_Item_VoidShard.uasset ← Collectible — rare, ending-related +│ +├── Pickups/ ← BP_Pickup_* children — world pickup actors +│ ├── BP_Pickup_Flashlight.uasset +│ ├── BP_Pickup_Pistol.uasset +│ ├── BP_Pickup_MedKit.uasset +│ ├── BP_Pickup_KeycardOmega.uasset +│ ├── BP_Pickup_Ammo_9mm.uasset +│ ├── BP_Pickup_Shotgun.uasset +│ ├── BP_Pickup_Ammo_Shells.uasset +│ ├── BP_Pickup_Battery.uasset +│ ├── BP_Pickup_AdrenalineSyringe.uasset +│ ├── BP_Pickup_SanityPill.uasset +│ ├── BP_Pickup_Crowbar.uasset +│ ├── BP_Pickup_KeycardAlpha.uasset +│ ├── BP_Pickup_KeycardBeta.uasset +│ ├── BP_Pickup_KeyWardens.uasset +│ ├── BP_Pickup_JournalPage.uasset +│ ├── BP_Pickup_PatientFile.uasset +│ ├── BP_Pickup_OldPhoto.uasset +│ └── BP_Pickup_VoidShard.uasset +│ +├── Actors/ ← World interaction actors +│ ├── BP_Door_Asylum.uasset ← Standard door (open/close/lock) +│ ├── BP_Door_OmegaWing.uasset ← Keycard-locked door +│ ├── BP_Door_Barricaded.uasset ← Crowbar-required door +│ ├── BP_Door_Void.uasset ← Reality-shifting door (adaptive env) +│ ├── BP_Chest_Supply.uasset ← Container inventory (lootable) +│ ├── BP_Desk_Drawer.uasset ← Container inventory (small) +│ ├── BP_FilingCabinet.uasset ← Container inventory (documents) +│ ├── BP_Puzzle_Fusebox.uasset ← Puzzle device (restore power) +│ ├── BP_Puzzle_PipeOrgan.uasset ← Puzzle device (play melody) +│ ├── BP_Puzzle_MorgueDrawers.uasset ← Puzzle device (find body) +│ ├── BP_HidingSpot_Locker.uasset ← Hiding spot +│ ├── BP_HidingSpot_UnderBed.uasset ← Hiding spot +│ ├── BP_HidingSpot_Wardrobe.uasset ← Hiding spot +│ ├── BP_Checkpoint_SafeRoom.uasset ← Checkpoint actor +│ ├── BP_Checkpoint_Mirror.uasset ← Checkpoint actor (thematic) +│ ├── BP_Usable_Lever.uasset ← Usable world object +│ ├── BP_Usable_Valve.uasset ← Usable world object +│ ├── BP_Usable_Button.uasset ← Usable world object +│ └── BP_DiegeticScreen_Monitor.uasset ← Diegetic display (security monitor) +│ +├── Weapons/ ← Held weapon actors +│ ├── BP_Pistol_Held.uasset ← Equipped pistol (I_UsableItem, I_Toggleable) +│ ├── BP_Flashlight_Held.uasset ← Equipped flashlight (I_UsableItem, I_Toggleable) +│ ├── BP_Shotgun_Held.uasset ← Equipped shotgun +│ └── BP_Crowbar_Held.uasset ← Equipped crowbar (tool + melee) +│ +├── UI/ ← Game-specific UI widgets +│ ├── WBP_GameHUDController.uasset ← Child of WBP_HUDController (game-specific layout) +│ ├── WBP_GameMainMenu.uasset ← Child of WBP_MainMenu (game title/branding) +│ ├── WBP_GamePauseMenu.uasset ← Child of WBP_PauseMenu +│ ├── WBP_GameInventoryMenu.uasset ← Child of WBP_InventoryMenu +│ ├── WBP_SplashHorror.uasset ← Child of WBP_SplashScreen +│ ├── WBP_JournalHorror.uasset ← Child of WBP_JournalDocumentViewer +│ ├── WBP_DeathScreen.uasset ← Custom death/void space screen +│ └── WBP_LoadingHorror.uasset ← Child of WBP_LoadingScreen (tips, atmosphere) +│ +├── DataAssets/ ← DA_* content instances +│ ├── DA_InputMapping_Horror_PC.uasset ← Input mapping profile for horror game +│ ├── DA_InputMapping_Horror_Xbox.uasset ← Console profile +│ ├── DA_Encounter_WardA_Patrol.uasset ← Encounter data +│ ├── DA_Encounter_ShadeAmbush.uasset ← Encounter data +│ ├── DA_Atmosphere_WardA_Standard.uasset← Atmosphere profile per area +│ ├── DA_Atmosphere_Basement_Dark.uasset +│ ├── DA_Atmosphere_VoidSpace.uasset +│ ├── DA_Scare_LockerJumpscare.uasset ← Scare event data +│ ├── DA_Scare_MirrorApparition.uasset +│ ├── DA_Scare_WindowFace.uasset +│ ├── DA_Scare_VoidWhisper.uasset +│ ├── DA_Puzzle_FuseboxData.uasset ← Puzzle data +│ ├── DA_Puzzle_PipeOrganData.uasset +│ ├── DA_Puzzle_MorgueDrawersData.uasset +│ ├── DA_Objective_FindKeys.uasset ← Objective data +│ ├── DA_Objective_RestorePower.uasset +│ ├── DA_Objective_EscapeAsylum.uasset +│ ├── DA_AdaptationRule_Horror.uasset ← Difficulty adaptation rules +│ ├── DA_BehaviourVariant_Patient.uasset ← AI behavior variants +│ ├── DA_BehaviourVariant_Shade.uasset +│ ├── DA_BehaviourVariant_Orderly.uasset +│ ├── DA_RoomAcoustic_WardA.uasset ← Room acoustic presets +│ ├── DA_RoomAcoustic_Basement.uasset +│ └── DA_RoomAcoustic_Void.uasset +│ +├── AI/ ← AI controllers, behavior trees, blackboards +│ ├── AI_PatientController.uasset ← AI controller for Patient enemy +│ ├── AI_ShadeController.uasset ← AI controller for Shade enemy +│ ├── AI_OrderlyController.uasset ← AI controller for Orderly enemy +│ ├── BT_PatientBehavior.uasset ← Behavior tree +│ ├── BT_ShadeBehavior.uasset +│ ├── BT_OrderlyBehavior.uasset +│ ├── BB_PatientData.uasset ← Blackboard data +│ ├── BB_ShadeData.uasset +│ ├── BB_OrderlyData.uasset +│ └── BP_PatrolPath_Asylum.uasset ← Patrol paths per area +│ +├── Narrative/ ← Narrative data + trigger volumes +│ ├── DA_Dialogue_Intro.uasset ← Dialogue data +│ ├── DA_Dialogue_SurvivorEncounter.uasset +│ ├── DA_Dialogue_EndingTruth.uasset +│ ├── DA_Cutscene_Opening.uasset ← Cutscene data +│ ├── DA_Cutscene_Ending.uasset +│ ├── BP_NarrativeTrigger_Intro.uasset ← Trigger volumes +│ ├── BP_NarrativeTrigger_VoidFirst.uasset +│ ├── BP_NarrativeTrigger_Ending.uasset +│ └── DA_LoreEntry_*.uasset ← Lore/data entries +│ +├── Encounters/ ← Procedural encounter spawners +│ ├── BP_EncounterSpawner_WardA.uasset +│ ├── BP_EncounterSpawner_Basement.uasset +│ └── BP_EncounterSpawner_Void.uasset +│ +├── Atmosphere/ ← Atmosphere + scare event actors +│ ├── BP_LightEvent_Flicker.uasset +│ ├── BP_ScareEvent_LockerBang.uasset +│ ├── BP_ScareEvent_Mirror.uasset +│ └── BP_AtmosphereController_WardA.uasset +│ +├── Audio/ ← Game-specific audio assets +│ ├── DA_AudioSettings_Horror.uasset ← Audio bus config +│ ├── BP_RoomAudioZone_WardA.uasset ← Room audio zone volumes +│ ├── BP_RoomAudioZone_Basement.uasset +│ └── BP_RoomAudioZone_Void.uasset +│ +├── Save/ ← Save system instances +│ ├── BP_Checkpoint_WardAEntry.uasset ← Placed checkpoint actors +│ ├── BP_Checkpoint_BasementGate.uasset +│ └── BP_Checkpoint_VoidThreshold.uasset +│ +├── Polish/ ← Tutorial, loading, credits +│ ├── BP_TutorialTrigger_Controls.uasset ← Tutorial prompt triggers +│ ├── BP_TutorialTrigger_Hiding.uasset +│ ├── BP_TutorialTrigger_Combat.uasset +│ └── BP_CreditsSequence.uasset ← Credits level/sequence +│ +├── Input/ ← Game-specific input overrides (if any) +│ └── (Empty unless overriding framework IMC_/IA_ assets) +│ +└── Maps/ ← ALL levels + ├── L_SplashScreen.umap ← Disclaimer/splash → auto-advances + ├── L_MainMenu.umap ← Title screen with 3D background + ├── L_Asylum_Entry.umap ← Tutorial zone — waking up + ├── L_Asylum_WardA.umap ← First major area + ├── L_Asylum_WardB.umap ← Second area (keycard gate) + ├── L_Asylum_Basement.umap ← Dark horror area + ├── L_Asylum_VoidSpace.umap ← Reality-shifted zone + ├── L_Asylum_WardensOffice.umap ← Climax area + ├── L_Ending_Truth.umap ← Final ending sequence + ├── L_Credits.umap ← Credits roll + └── L_Test_Sandbox.umap ← Developer testing sandbox +``` + +--- + +## Scene Loading Flow (Complete) + +``` +BOOT SEQUENCE +═══════════════════════════════════════════════════════════ + +[Engine Init] + │ + └─ GI_GameFramework (Framework/Core/) — kernel boots + ├─ Initialize all SS_ subsystems: + │ ├─ SS_SaveManager (35) — mount save directory + │ ├─ SS_SettingsSystem (105) — load persistent settings + │ ├─ SS_EnhancedInputManager (128) — load input profiles + │ ├─ SS_AudioManager (132) — init MetaSound buses + │ ├─ SS_AchievementSystem (103) — load achievement state + │ └─ SS_UIManager (44) — create menu widget cache + │ + ├─ DA_GameTagRegistry (01) — validate all tags + ├─ Set Game Phase → Loading + └─ OpenLevel → L_SplashScreen + │ + ▼ + ┌─────────────────────────────────────┐ + │ L_SplashScreen │ (5–8 seconds, skippable) + │ ├─ Studio logo (fade in/out) │ + │ ├─ UE5 logo │ + │ ├─ "Project Void" title card │ + │ │ └─ Audio: MS_MusicBus plays │ + │ │ menu_ambient_loop │ + │ ├─ Health warning │ + │ └─ Auto-advance or Skip → │ + └──────────────┬──────────────────────┘ + │ + ▼ + ┌─────────────────────────────────────┐ + │ L_MainMenu │ + │ ├─ WBP_GameMainMenu displayed │ + │ ├─ 3D background: asylum hallway │ + │ ├─ Menu options: │ + │ │ ├─ New Game → L_Asylum_Entry │ + │ │ ├─ Continue → Load latest save │ + │ │ ├─ Load Game → Save slot UI │ + │ │ ├─ Settings → WBP_SettingsMenu │ + │ │ ├─ Credits → L_Credits │ + │ │ └─ Quit → Confirm → Quit │ + │ └─ Audio: MS_MusicBus menu_theme │ + └──────────────┬──────────────────────┘ + │ New Game + ▼ + ┌─────────────────────────────────────┐ + │ LOADING SCREEN │ + │ ├─ WBP_LoadingHorror displayed │ + │ ├─ Progress bar + atmosphere tip │ + │ ├─ SS_SaveManager.ResetGameState() │ + │ ├─ GI_GameFramework.SetPhase │ + │ │ (InGame) on level loaded │ + │ └─ Audio: crossfade to game amb │ + └──────────────┬──────────────────────┘ + │ + ▼ +GAMEPLAY LOOP +═══════════════════════════════════════════════════════════ + + ┌─────────────────────────────────────┐ + │ L_Asylum_Entry (Tutorial) │ + │ ├─ Player wakes in padded cell │ + │ ├─ Tutorial triggers: │ + │ │ ├─ Move/Look (BP_Tutorial_*) │ + │ │ ├─ Interact (pick up note) │ + │ │ ├─ Crouch (vent) │ + │ │ └─ Hide (first patient sight) │ + │ ├─ First objective: "Find a way │ + │ │ out of this wing" │ + │ └─ Exit → L_Asylum_WardA │ + └──────────────┬──────────────────────┘ + │ + ▼ + ┌─────────────────────────────────────┐ + │ L_Asylum_WardA (Exploration) │ + │ ├─ Find: Flashlight (dark hallway) │ + │ ├─ Find: Pistol (security office) │ + │ ├─ Puzzle: Restore fusebox power │ + │ ├─ Encounter: Patient enemy │ + │ ├─ Find: Keycard Alpha │ + │ ├─ Atmosphere: Flickering lights, │ + │ │ ambient whispers, room zones │ + │ └─ Exit → L_Asylum_WardB │ + └──────────────┬──────────────────────┘ + │ + ▼ + ┌─────────────────────────────────────┐ + │ L_Asylum_WardB (Horror Ramp) │ + │ ├─ Keycard-gated doors │ + │ ├─ Find: Shotgun (morgue) │ + │ ├─ Puzzle: Pipe organ melody │ + │ ├─ Encounter: Orderly enemy │ + │ ├─ First Void Space shift │ + │ │ └─ Reality warps: walls bleed, │ + │ │ corridors loop │ + │ ├─ Find: Keycard Beta │ + │ └─ Exit → L_Asylum_Basement │ + └──────────────┬──────────────────────┘ + │ + ▼ + ┌─────────────────────────────────────┐ + │ L_Asylum_Basement (Dark Horror) │ + │ ├─ No lights — battery management │ + │ ├─ Puzzle: Morgue drawer sequence │ + │ ├─ Encounter: Shade enemy │ + │ ├─ Stress system peaks — │ + │ │ hallucinations trigger │ + │ ├─ Find: Warden's Key │ + │ └─ Exit → L_Asylum_WardensOffice │ + └──────────────┬──────────────────────┘ + │ + ▼ + ┌─────────────────────────────────────┐ + │ L_Asylum_VoidSpace (Climax) │ + │ ├─ Reality fully broken │ + │ ├─ Chase sequence (Shade pursues) │ + │ ├─ Ending accumulator evaluates │ + │ │ choices made during game │ + │ └─ Exit → L_Ending_Truth │ + └──────────────┬──────────────────────┘ + │ + ▼ + ┌─────────────────────────────────────┐ + │ L_Ending_Truth (Resolution) │ + │ ├─ Final cutscene plays │ + │ ├─ Ending determined by: │ + │ │ ├─ VoidShards collected │ + │ │ ├─ Survivors saved/killed │ + │ │ ├─ Sanity level at end │ + │ │ └─ Key choices made │ + │ └─ Auto-advance → L_Credits │ + └──────────────┬──────────────────────┘ + │ + ▼ + ┌─────────────────────────────────────┐ + │ L_Credits │ + │ ├─ Scroll credits + atmospheric │ + │ │ post-credit scene │ + │ ├─ Achievement unlocks fire │ + │ └─ Return → L_MainMenu │ + └─────────────────────────────────────┘ + + +PAUSE / DEATH / SAVE BRANCHES (any gameplay level) +═══════════════════════════════════════════════════════════ + +PRESS ESC → SS_UIManager.PushMenu(PauseMenu) + ├─ GI_GameFramework.SetPhase(Paused) + ├─ SS_EnhancedInputManager.SetInputModeUIOnly() + ├─ WBP_HUDController hides diegetic elements + ├─ WBP_GamePauseMenu shows (Resume/Save/Load/Settings/Quit) + └─ Resume → PopMenu → SetPhase(InGame) → restore input + +PLAYER DIES → BPC_DeathHandlingSystem (39) + ├─ Death animation plays + ├─ BPC_StateManager.ForcePushState(Death) + ├─ Check if VoidSpace conditions met: + │ ├─ Yes → BPC_AltDeathSpaceSystem.EnterVoidSpace() + │ │ └─ Load L_Asylum_VoidSpace + solve puzzle to return + │ └─ No → BPC_PlayerRespawnSystem.RespawnAtCheckpoint() + │ └─ Load last checkpoint → restore state + ├─ BPC_PersistentCorpseSystem leaves corpse + └─ BPC_RunHistoryTracker logs death + +CHECKPOINT TOUCHED → BP_Checkpoint (37) + ├─ SS_SaveManager.CreateAutoSave() + ├─ BPC_PersistentWorldStateRecorder captures world state + │ └─ Door states, item pickups, enemy positions, flags + ├─ WBP_NotificationToast shows "Checkpoint Reached" + └─ Screen vignette pulses briefly +``` + +--- + +## Framework System Coverage Map + +Each game asset proves a specific framework system works. Below: every framework system → which game asset demonstrates it. + +### 01-core (Foundation — 7 systems) +| # | Framework System | Game Asset That Uses It | +|---|-----------------|------------------------| +| 01 | DA_GameTagRegistry | All game items/pickups (tags registered) | +| 02 | FL_GameUtilities | FL_HorrorUtilities (game-specific helpers) | +| 03 | I_InterfaceLibrary | All items, doors, enemies (I_Interactable, I_Damageable, I_UsableItem, I_Lockable, I_Persistable) | +| 04 | GI_GameFramework | GI_HorrorGame (child — sets TagRegistry, initializes game) | +| 05 | GM_CoreGameMode | GM_HorrorGameMode (child — horror-specific rules, deaths) | +| 06 | GS_CoreGameState | GS_HorrorGameState (tracks horror-specific global flags) | +| 07 | DA_ItemData | ALL DA_Item_* assets (13 items) | + +### 02-player (Player State — 8 systems) +| # | Framework System | How Demonstrated | +|---|-----------------|-----------------| +| 08 | BPC_HealthSystem | Player takes damage from enemies → health decreases → death | +| 09 | BPC_StaminaSystem | Sprinting drains stamina; AdrenalineSyringe restores it | +| 10 | BPC_StressSystem | Darkness, enemy proximity, hallucinations increase stress tiers | +| 11 | BPC_MovementStateSystem | Walk/Sprint/Crouch/Sneak — GASP locomotion states | +| 12 | BPC_HidingSystem | Lockers, under beds, wardrobes — hide from enemies | +| 13 | BPC_EmbodimentSystem | First-person body visible; arm IK when holding items | +| 14 | BPC_CameraStateLayer | FOV changes: combat zoom, hide peek, inspection zoom | +| 15 | BPC_PlayerMetricsTracker | Accuracy tracking per weapon, playstyle classification | + +### 03-interaction (World Interaction — 8 systems) +| # | Framework System | Game Asset | +|---|-----------------|-----------| +| 16 | BPC_InteractionDetector | All pickups, doors, puzzles, hiding spots | +| 17 | I_HidingSpot | BP_HidingSpot_Locker, _UnderBed, _Wardrobe | +| 18 | BPC_DiegeticDisplay | BP_DiegeticScreen_Monitor (security camera feed) | +| 19 | BP_DoorActor | BP_Door_Asylum, _OmegaWing, _Barricaded, _Void | +| 20 | BP_PuzzleDeviceActor | BP_Puzzle_Fusebox, _PipeOrgan, _MorgueDrawers | +| 21 | BPC_ContextualTraversalSystem | Vaulting over gurneys, mantling debris | +| 22 | BPC_PhysicsDragSystem | Dragging bodies/crates to climb or block doors | +| 23 | BPC_UsableWorldObjectSystem | Levers, valves, buttons (power, gas, elevator) | + +### 04-inventory (Inventory — 11 systems) +| # | Framework System | Demonstrated By | +|---|-----------------|----------------| +| 24 | BPC_ContainerInventory | BP_Chest_Supply, BP_Desk_Drawer, BP_FilingCabinet | +| 25 | BP_ItemPickup | All 18 BP_Pickup_* actors | +| 26 | BPC_ActiveItemSystem | Quick-slot cycling through weapons/tools | +| 27 | BPC_CollectibleTracker | Old Photos, Void Shards (tracked, set bonuses) | +| 28 | BPC_ConsumableSystem | MedKit, Battery, Adrenaline, Sanity Pill use | +| 29 | BPC_DocumentArchiveSystem | Journal Pages, Patient Files (readable/journal) | +| 30 | BPC_EquipmentSlotSystem | PrimaryWeapon (pistol/shotgun), Tool (flashlight/crowbar) | +| 31 | BPC_InventorySystem | Full inventory grid with all items | +| 32 | BPC_ItemCombineSystem | Combine Battery+Flashlight to restore charge | +| 33 | BPC_JournalSystem | Active objectives displayed, completions logged | +| 34 | BPC_KeyItemSystem | Keycards, Warden's Key — door unlocking, protection from loss | + +### 05-saveload (Save/Load — 9 systems) +| # | Framework System | Demonstrated By | +|---|-----------------|----------------| +| 35 | SS_SaveManager | Auto-save at checkpoints, manual save, load slots | +| 36 | I_Persistable | All doors, pickups, enemies (save/restore state) | +| 37 | BP_Checkpoint | BP_Checkpoint_SafeRoom, _Mirror | +| 38 | BPC_AltDeathSpaceSystem | Void Space on death — enter, explore, find exit | +| 39 | BPC_DeathHandlingSystem | Death → respawn or void space → state restoration | +| 40 | BPC_PersistentCorpseSystem | Enemy corpses persist across save/load/respawn | +| 41 | BPC_PersistentWorldStateRecorder | Door states, picked items, enemy positions saved | +| 42 | BPC_PlayerRespawnSystem | Respawn at last checkpoint after death | +| 43 | BPC_RunHistoryTracker | Death count, playtime, checkpoints per run | + +### 06-ui (UI — 14 systems) +| # | Framework System | Game Override | +|---|-----------------|--------------| +| 44 | SS_UIManager | Menu stack manages all game menus | +| 45 | WBP_AccessibilityUI | Subtitles for whispered voices + accessibility options | +| 46 | WBP_DiegeticHUDFrame | Health/stress/stamina displayed on wristwatch | +| 47 | WBP_HUDController | WBP_GameHUDController (horror-themed layout) | +| 48 | WBP_InteractionPromptDisplay | "Pick up [Item]" / "Open [Door]" prompts | +| 49 | WBP_InventoryMenu | WBP_GameInventoryMenu (grid with horror styling) | +| 50 | WBP_JournalDocumentViewer | WBP_JournalHorror (reads documents + photos) | +| 51 | WBP_MainMenu | WBP_GameMainMenu ("Project Void" title + asylum background) | +| 52 | WBP_MenuFlowController | Handles back-nav across all menus | +| 53 | WBP_NotificationToast | "Checkpoint Reached" / "New Objective" toasts | +| 54 | WBP_ObjectiveDisplay | "Find the Warden's Key" / "Escape the Asylum" | +| 55 | WBP_PauseMenu | WBP_GamePauseMenu | +| 56 | WBP_ScreenEffectController | Damage vignette, stress blur, void distortion | +| 57 | WBP_SettingsMenu | Volume sliders → SS_AudioManager, control rebinding | + +### 07-narrative (Narrative — 11 systems) +| # | Framework System | Demonstrated By | +|---|-----------------|----------------| +| 58 | BPC_NarrativeStateSystem | Chapter flags: "WardA_Complete", "MetSurvivor" | +| 59 | BPC_ObjectiveSystem | 3 main objectives + optional side objectives | +| 60 | BPC_DialoguePlaybackSystem | Survivor dialogue, whispered void voices | +| 61 | BPC_DialogueChoiceSystem | Player response choices to survivor NPC | +| 62 | BPC_BranchingConsequenceSystem | Choices affect ending, NPC survival, item availability | +| 63 | BPC_TrialScenarioSystem | Timed escape from Void Space | +| 64 | BPC_CutsceneBridge | Opening wake-up cutscene + ending cinematics | +| 65 | BPC_LoreUnlockSystem | Patient files, journal pages unlock lore entries | +| 66 | DA_NarrativeDataAssets | DA_Dialogue_*, DA_Cutscene_*, DA_Objective_* | +| 67 | BP_NarrativeTriggerVolume | BP_NarrativeTrigger_Intro, _VoidFirst, _Ending | +| 68 | BPC_EndingAccumulator | Tracks VoidShards, survivors, sanity → determines ending | + +### 08-weapons (Weapons & Combat — 11 systems) +| # | Framework System | Demonstrated By | +|---|-----------------|----------------| +| 69 | BP_WeaponBase | BP_Pistol_Held, BP_Shotgun_Held | +| 70 | BPC_AmmoComponent | Pistol (9mm pool), Shotgun (shell pool) | +| 71 | BPC_CombatFeedbackComponent | Hit markers on enemy damage, kill confirm | +| 72 | BPC_DamageReceptionSystem | Enemy damage → resistance → health reduction | +| 73 | BPC_DeathCauseTracker | Logs: "Killed by Patient (melee)", "Killed by Shade (void)" | +| 74 | BPC_FirearmSystem | Pistol hitscan, Shotgun pellet spread | +| 75 | BPC_HitReactionSystem | Enemies flinch/stagger on hit | +| 76 | BPC_MeleeSystem | Crowbar swing (block, heavy attack) | +| 77 | BPC_RecoilSystem | Pistol recoil kick, Shotgun heavy kick | +| 78 | BPC_ReloadSystem | Pistol tactical reload, Shotgun shell-by-shell | +| 79 | BPC_ShieldDefenseSystem | (Optional) Warden's riot shield pickup | + +### 09-ai (AI — 9 systems) +| # | Framework System | Demonstrated By | +|---|-----------------|----------------| +| 80 | BP_EnemyBase | BP_Enemy_Patient, BP_Enemy_Shade, BP_Enemy_Orderly | +| 81 | BP_PatrolPath | BP_PatrolPath_* throughout asylum hallways | +| 82 | BPC_AlertSystem | Suspicious→Alerted→Combat transitions | +| 83 | BPC_AIStateMachine | Patrol/Search/Combat/Flee state machine | +| 84 | AI_BaseAgentController | AI_PatientController, AI_ShadeController, AI_OrderlyController | +| 85 | BB_AgentBoard | BB_PatientData, BB_ShadeData, BB_OrderlyData | +| 86 | BPC_AIMemorySystem | Last known player location, investigation points | +| 87 | BPC_AIPerceptionSystem | Sight (flashlight increases visibility), hearing (running attracts) | +| 88 | BPC_BehaviourVariantSelector | Patient: Aggressive/Curious/Erratic variants | + +### 10-adaptive (Adaptive — 15 systems) +| # | Framework System | Demonstrated By | +|---|-----------------|----------------| +| 89 | BPC_DifficultyManager | Player deaths → easier encounters; success → harder | +| 90 | BPC_FearSystem | AI fear of flashlight; player fear affects stress | +| 91 | BPC_PerformanceScaler | LOD scaling in large ward areas | +| 92 | BPC_ProceduralEncounter | BP_EncounterSpawner_* (dynamic enemy placement) | +| 93 | BPC_AdaptiveEnvironmentDirector | Room mutations: hallway reconfigures, doors disappear | +| 94 | BPC_AtmosphereStateController | WardA: tense → WardB: horror → Void: nightmare | +| 95 | BPC_AudioAtmosphereController | [DEPRECATED — replaced by SS_AudioManager] | +| 96 | BPC_LightEventController | Flickering lights, bulb explosions, emergency red | +| 97 | BPC_MemoryDriftSystem | Visual distortions: walls breathing, text shifting | +| 98 | BPC_PacingDirector | Intensity bands: exploration→tension→encounter→recovery | +| 99 | BPC_PlaystyleClassifier | Aggressive (shoot everything) vs Stealthy (hide) detection | +| 100 | BPC_RareEventSystem | Rare whisper events, ghostly apparitions | +| 101 | BPC_ScareEventSystem | Locker bang, mirror face, window hand slam | +| 132 | SS_AudioManager | All audio routing, bus management, room zone switching | +| 133 | BP_RoomAudioZone | BP_RoomAudioZone_WardA, _Basement, _Void | +| 134 | DA_AudioSettings | DA_AudioSettings_Horror (mix levels, ducking) | +| 135 | DA_RoomAcousticPreset | DA_RoomAcoustic_WardA, _Basement, _Void (reverb/occlusion) | + +### 11-meta (Meta — 2 systems) +| # | Framework System | Demonstrated By | +|---|-----------------|----------------| +| 102 | BPC_ProgressStatTracker | Total playtime, collectibles found, enemies killed | +| 103 | SS_AchievementSystem | "First Light" (flashlight), "Untouchable" (no-damage run) | + +### 12-settings (Settings — 2 systems) +| # | Framework System | Demonstrated By | +|---|----------------|----------------| +| 104 | BPC_AccessibilitySettings | Subtitle toggle, colorblind mode, controller remap | +| 105 | SS_SettingsSystem | Audio/Video/Controls persistent settings | + +### 13-polish (Polish — 9 systems) +| # | Framework System | Demonstrated By | +|---|-----------------|----------------| +| 106 | BPC_AnalyticsTracker | Session metrics: time in each area, death locations | +| 107 | BPC_DevCheatManager | God mode, give item, teleport to area (dev testing) | +| 108 | BPC_ErrorHandler | Crash logging, safe recovery on level load failure | +| 109 | BPC_FPSCounter | Dev-only FPS overlay | +| 110 | BPC_LoadingScreen | WBP_LoadingHorror (progress bar + horror tips) | +| 111 | BPC_TutorialSystem | BP_TutorialTrigger_Controls, _Hiding, _Combat | +| 112 | WBP_CreditsScreen | L_Credits level + scroll widget | +| 113 | WBP_DebugMenu | Toggle debug: show AI paths, perception, stress values | +| 114 | WBP_SplashScreen | WBP_SplashHorror (studio logo + title + health warning) | + +### 15-input (Input) +| # | Framework System | Demonstrated By | +|---|-----------------|----------------| +| 128 | SS_EnhancedInputManager | All input contexts (Default, Hiding, WristwatchUI, UI, Inspection) | + +### 16-state (State Management) +| # | Framework System | Demonstrated By | +|---|-----------------|----------------| +| 130 | BPC_StateManager | Hiding blocks fire; death blocks interaction; cutscene blocks all | +| 131 | DA_StateGatingTable | Designer-configurable state permission rules | + +--- + +## Build Order (Dependency-Safe) + +Follow this order when creating uasset files in UE5: + +| Phase | Content | Depends On | Approx. Assets | +|-------|---------|-----------|:---:| +| 0 | **Project Settings** — Plugins, GameplayTags, Asset Manager, Collision | *(none)* | — | +| 1 | **Core Classes** — GI_HorrorGame, GM_HorrorGameMode, PC_HorrorController, PS_HorrorPlayerState, GS_HorrorGameState | Framework C++ classes | 5 | +| 2 | **Maps** — L_SplashScreen, L_MainMenu, L_Test_Sandbox | Core Classes | 3 | +| 3 | **Input** — DA_InputMapping_Horror_PC (_Xbox) + IMC/IA references | SS_EnhancedInputManager | 2 | +| 4 | **Player Character** — BP_HorrorPlayerCharacter + all BPC_ components | Core, Maps, Input | 1 | +| 5 | **Data Assets (Items)** — All DA_Item_* assets | DA_ItemData | 13 | +| 6 | **Pickups** — All BP_Pickup_* actors | BP_ItemPickup, DA_Item_* | 18 | +| 7 | **Doors & Actors** — All BP_Door_*, BP_HidingSpot_* | BP_DoorActor, I_HidingSpot | 12 | +| 8 | **Containers** — BP_Chest_Supply, BP_Desk_Drawer, BP_FilingCabinet | BPC_ContainerInventory | 3 | +| 9 | **Puzzles** — BP_Puzzle_Fusebox, _PipeOrgan, _MorgueDrawers | BP_PuzzleDeviceActor | 3 | +| 10 | **Weapons (Held)** — BP_Pistol_Held, BP_Shotgun_Held, BP_Flashlight_Held, BP_Crowbar_Held | BP_WeaponBase | 4 | +| 11 | **UI Widgets** — All WBP_Game* widgets | SS_UIManager, WBP_* parents | 8 | +| 12 | **Enemies** — BP_Enemy_Patient, _Shade, _Orderly + AI controllers + BT + BB | BP_EnemyBase | 12 | +| 13 | **Narrative** — DA_Dialogue_*, DA_Cutscene_*, DA_Objective_*, trigger volumes, lore | DA_NarrativeDataAssets | 12+ | +| 14 | **Data Assets (Content)** — Encounters, Atmosphere, Scares, Puzzles, Objectives, Behaviour, Acoustic | DA_* parents | 20+ | +| 15 | **Checkpoints** — BP_Checkpoint_SafeRoom, _Mirror | BP_Checkpoint | 2 | +| 16 | **Room Audio Zones** — BP_RoomAudioZone_* | BP_RoomAudioZone | 3 | +| 17 | **Encounter Spawners** — BP_EncounterSpawner_* | BPC_ProceduralEncounter | 3 | +| 18 | **Atmosphere Actors** — BP_LightEvent_*, BP_ScareEvent_*, BP_AtmosphereController_* | BPC_* parent | 6 | +| 19 | **Tutorial Triggers** — BP_TutorialTrigger_* | BPC_TutorialSystem | 3 | +| 20 | **Polish** — BP_CreditsSequence, dev cheats, debug menu | Framework | 3 | +| 21 | **Full Levels** — L_Asylum_Entry → _WardA → _WardB → _Basement → _VoidSpace → _WardensOffice → _Ending → _Credits | All previous | 8 | + +--- + +## Documentation Map + +Each `docs/game/` file explains how to build a specific section of the prototype: + +| Document | Covers | Build Phase | +|----------|--------|:----------:| +| [`GAMEINDEX.md`](GAMEINDEX.md) | **THIS FILE** — master reference | — | +| [`README.md`](README.md) | How to use these docs + framework separation rule | — | +| [`core-gameinstance.md`](core-gameinstance.md) | GI_HorrorGame setup — session init, save slot, subsystems | 1 | +| [`core-gamemode.md`](core-gamemode.md) | GM_HorrorGameMode setup — spawn rules, chapter transitions, death routing | 1 | +| [`core-controller-state.md`](core-controller-state.md) | PC_HorrorController, PS_HorrorPlayerState, GS_HorrorGameState | 1 | +| [`scene-flow.md`](scene-flow.md) | Complete scene loading: Splash → Menu → Game → Pause → Death → Credits | 2 | +| [`playercharacter.md`](playercharacter.md) | BP_HorrorPlayerCharacter — all 15 BPC_ components wired, GASP integration | 4 | +| [`levels.md`](levels.md) | All 11 level maps — layout, objectives, encounters, atmosphere | 2, 21 | +| [`ui-overrides.md`](ui-overrides.md) | WBP_GameHUDController, WBP_GameMainMenu, WBP_GamePauseMenu, WBP_SplashHorror, WBP_LoadingHorror, WBP_JournalHorror, WBP_DeathScreen | 11 | +| [`items-index.md`](items-index.md) | All 13 DA_Item_* + 18 BP_Pickup_* — creation walkthroughs | 5, 6 | +| [`item-flashlight.md`](item-flashlight.md) | Flashlight Tool — full build (EXISTING) | 5, 6, 10 | +| [`item-pistol.md`](item-pistol.md) | Pistol Weapon — full build (EXISTING) | 5, 6, 10 | +| [`item-medkit.md`](item-medkit.md) | MedKit Consumable — full build (EXISTING) | 5, 6 | +| [`item-keycard.md`](item-keycard.md) | Keycard Key Item — full build (EXISTING) | 5, 6 | +| [`weapons-index.md`](weapons-index.md) | All held weapons — pistol, shotgun, flashlight, crowbar — wiring | 10 | +| [`enemies-index.md`](enemies-index.md) | All enemies — Patient, Shade, Orderly — AI controllers, BT, perception | 12 | +| [`narrative-progression.md`](narrative-progression.md) | Objectives, dialogue, cutscenes, ending accumulator, branching | 13 | +| [`atmosphere-audio.md`](atmosphere-audio.md) | Atmosphere profiles, scares, lighting, room audio zones, adaptive environment | 14, 16, 17, 18 | +| [`save-checkpoints.md`](save-checkpoints.md) | Save system, checkpoints, death loop, void space, persistence | 15 | +| [`polish-loading-credits.md`](polish-loading-credits.md) | Tutorials, loading screen, credits, debug, analytics | 19, 20 | +| [`state-gating-examples.md`](state-gating-examples.md) | DA_StateGatingTable game-specific rules | 14 | + +--- + +## Notes for Expansion + +### Short-Term (Finish the Prototype) +- [ ] Create placeholder meshes (whitebox) for all items, weapons, enemies +- [ ] Record placeholder audio (or use UE5 starter content) +- [ ] Implement all 18 pickup actors following the 4 existing item examples +- [ ] Build full WBP_GameHUDController with horror styling +- [ ] Create 3 enemy types with behavior trees +- [ ] Wire all 11 levels with objectives and transitions +- [ ] Implement save/load at checkpoints + +### Medium-Term (Polish) +- [ ] Replace whitebox meshes with final art +- [ ] Add full MetaSounds audio (150+ triggers per sound catalog) +- [ ] Implement all 14 animation notifies for GASP +- [ ] Add multiplayer support (server-authoritative replication) +- [ ] Create platform-specific input profiles (Xbox, PS5) +- [ ] Add accessibility: subtitles, colorblind, haptic feedback profiles + +### Long-Term (Full Game) +- [ ] Write complete narrative with branching dialogue +- [ ] Design 10+ puzzle devices +- [ ] Create 20+ scare events per atmosphere catalog +- [ ] Implement adaptive difficulty with player metrics +- [ ] Add collectible tracking with achievement unlocks +- [ ] Create ending variations (3-5 endings) +- [ ] Implement New Game+ with harder variants +- [ ] Localize all text into 8+ languages + +### Framework Separation Checklist +- [ ] `Content/Framework/` contains ONLY framework systems (never modified) +- [ ] `Content/Game/` contains ALL game-specific content +- [ ] Game Blueprints are children of Framework Blueprints (never modify parent) +- [ ] Game Data Assets are instances of Framework Data Asset types +- [ ] Game references framework via interfaces and dispatchers (no hard refs) +- [ ] Project Settings point to Framework GI_GameFramework as Game Instance +- [ ] Game Maps reference GM_HorrorGameMode as GameMode override + +--- + +*GAMEINDEX v1.0 — Master reference for Project Void horror game prototype. All 135 framework systems covered by at least one game asset. Follow the Build Order phases. Never modify Content/Framework/ files.* diff --git a/docs/game/README.md b/docs/game/README.md index ec33bda..90a004a 100644 --- a/docs/game/README.md +++ b/docs/game/README.md @@ -1,32 +1,86 @@ -# Game Examples — Concrete Blueprint Walkthroughs +# Game Examples — "Project Void" Horror Game Prototype **Directory:** `docs/game/` -**Purpose:** Step-by-step examples of building specific game items using the PG_Framework. Each document shows exactly what to create, what components to add, what Blueprint nodes to wire, and how to verify it works. +**Purpose:** Complete reference documentation for building the "Project Void" psychological horror FPS prototype using the UE5 Modular Game Framework. Every document shows exactly what Blueprint assets to create, how to wire them, and how to integrate with framework systems. -These documents are **separate from the framework** — they belong in a `Content/Game/` folder in your project, not in `Content/Framework/`. The framework provides the rules and systems; these examples show how to use them. +These documents are **separate from the framework** — they describe assets that go in a `Content/Game/` folder, not `Content/Framework/`. The framework provides the rules and systems; these examples show how to build a complete game with them. --- -## How to Use These Examples +## Master Index -Each example follows the same structure: - -1. **DA_ItemData** — Create the Data Asset that defines the item's identity -2. **BP_ItemPickup** — Create the world actor that represents the item -3. **Optional: BP_UsableItem** — If the item has active-use behavior (flashlight toggle, weapon fire), create a specialized actor with `I_UsableItem` -4. **Blueprints & Wiring** — Exact node-by-node graphs with screenshots descriptions -5. **Verification** — How to test it works in PIE +| Document | Covers | Status | +|----------|--------|:---:| +| **[GAMEINDEX.md](GAMEINDEX.md)** | **MASTER REFERENCE** — Complete game structure, all 170+ assets, all 135 framework systems mapped, 21-phase build order, scene flow | ✅ | +| **[README.md](README.md)** | **THIS FILE** — How to use these docs, framework separation rules | ✅ | --- -## Example Index +## Core Game Systems (Build Phase 1) -| Example | Item Type | Complexity | What You Learn | -|---------|-----------|-----------|----------------| -| [Flashlight Tool](item-flashlight.md) | `Tool` | Medium | Data Asset setup, `I_UsableItem`, `BP_ItemPickup` Construction Script, soft reference resolution, toggleable function via `I_Toggleable` | -| [Pistol Weapon](item-pistol.md) | `Weapon` | High | `Equipment Data`, `BPC_AmmoComponent`, `I_Equippable`, frame-driven fire, ammo consumption | -| [MedKit Consumable](item-medkit.md) | `Consumable` | Low | `Consumable Data`, quick-slot use, `BPC_HealthSystem` integration | -| [Keycard Key Item](item-keycard.md) | `KeyItem` | Low | `bIsKeyItem`, `I_Lockable` interaction, door unlocking | +| Document | Asset(s) | Systems Demonstrated | +|----------|----------|---------------------| +| [core-gameinstance.md](core-gameinstance.md) | `GI_HorrorGame` | `GI_GameFramework` (04) — boot sequence, subsystem init, save slots | +| [core-gamemode.md](core-gamemode.md) | `GM_HorrorGameMode` | `GM_CoreGameMode` (05) — spawn rules, death routing, chapter transitions | +| [core-controller-state.md](core-controller-state.md) | `PC_HorrorController`, `PS_HorrorPlayerState`, `GS_HorrorGameState` | PlayerController, PlayerState, GameState — input routing, player data, global session state | + +--- + +## Gameplay Systems (Build Phases 2-6) + +| Document | Assets | Systems Demonstrated | +|----------|--------|---------------------| +| [scene-flow.md](scene-flow.md) | All 11 `.umap` levels — scene transitions | `GI_GameFramework`, `GM_CoreGameMode`, `SS_UIManager`, `BPC_LoadingScreen`, `WBP_SplashScreen` — complete boot-to-credits scene loading | +| [playercharacter.md](playercharacter.md) | `BP_HorrorPlayerCharacter` (27 components) | All `02-player` (8), `03-interaction` (1), `04-inventory` (8), `08-weapons` (5), `16-state` (1) — the single most important game asset | +| [levels.md](levels.md) | All 11 level maps — layout, objectives, encounters | `GM_CoreGameMode`, `BP_DoorActor`, `BP_Checkpoint`, `BP_NarrativeTriggerVolume`, `BPC_ProceduralEncounter` | +| [ui-overrides.md](ui-overrides.md) | 8 `WBP_Game*` widgets | All 14 `06-ui` systems — MainMenu, HUD, PauseMenu, Inventory, Journal, SplashScreen, DeathScreen, LoadingScreen | + +--- + +## Content Systems (Build Phases 5-13) + +| Document | Assets | Systems Demonstrated | +|----------|--------|---------------------| +| [items-index.md](items-index.md) | 13 `DA_Item_*` + 18 `BP_Pickup_*` characters | `DA_ItemData` (07), `BP_ItemPickup` (25), `BPC_InventorySystem` (31), all item types | +| **[item-flashlight.md](item-flashlight.md)** | `DA_Item_Flashlight` + `BP_Pickup_Flashlight` + `BP_Flashlight_Held` | Tool, `I_UsableItem`, `I_Toggleable` — FULL BUILD | +| **[item-pistol.md](item-pistol.md)** | `DA_Item_Pistol` + `BP_Pickup_Pistol` + `BP_Pistol_Held` | Weapon, hitscan, ammo, recoil, reload — FULL BUILD | +| **[item-medkit.md](item-medkit.md)** | `DA_Item_MedKit` + `BP_Pickup_MedKit` | Consumable, health restore — FULL BUILD | +| **[item-keycard.md](item-keycard.md)** | `DA_Item_KeycardOmega` + `BP_Pickup_KeycardOmega` + `BP_Door_OmegaWing` | KeyItem, `I_Lockable`, door unlocking — FULL BUILD | +| [weapons-index.md](weapons-index.md) | `BP_Pistol_Held`, `BP_Shotgun_Held`, `BP_Flashlight_Held`, `BP_Crowbar_Held` | `BP_WeaponBase` (69), `BPC_FirearmSystem` (74), `BPC_MeleeSystem` (76), pellet spread, shell reload | +| [enemies-index.md](enemies-index.md) | 3 enemy types + 1 NPC + 3 AI controllers + 3 behavior trees + 3 blackboards | All `09-ai` (9): `BP_EnemyBase`, `BPC_AlertSystem`, `BPC_AIStateMachine`, `BPC_AIPerceptionSystem`, `BPC_AIMemorySystem` | + +--- + +## Narrative & Progression (Build Phase 13) + +| Document | Assets | Systems Demonstrated | +|----------|--------|---------------------| +| [narrative-progression.md](narrative-progression.md) | 4 `DA_Dialogue_*`, 4 `DA_Objective_*`, 2 `DA_Cutscene_*`, 4 `BP_NarrativeTriggerVolume`, lore entries | All `07-narrative` (11): `BPC_NarrativeStateSystem`, `BPC_ObjectiveSystem`, `BPC_DialoguePlaybackSystem`, `BPC_DialogueChoiceSystem`, `BPC_BranchingConsequenceSystem`, `BPC_CutsceneBridge`, `BPC_EndingAccumulator` — 3 endings | + +--- + +## Atmosphere, Audio & Scares (Build Phases 14-18) + +| Document | Assets | Systems Demonstrated | +|----------|--------|---------------------| +| [atmosphere-audio.md](atmosphere-audio.md) | 6 `DA_AtmosphereProfile`, 6 `DA_RoomAcousticPreset`, 6 `DA_ScareEvent`, 6 `BP_RoomAudioZone`, light events, memory drift | All `10-adaptive` (15) including `132-135` MetaSounds Audio: `SS_AudioManager`, `BP_RoomAudioZone`, `BPC_ScareEventSystem`, `BPC_LightEventController`, `BPC_MemoryDriftSystem`, `BPC_PacingDirector`, `BPC_AdaptiveEnvironmentDirector` | + +--- + +## Save, Death & Persistence (Build Phase 15) + +| Document | Assets | Systems Demonstrated | +|----------|--------|---------------------| +| [save-checkpoints.md](save-checkpoints.md) | 4 `BP_Checkpoint_*`, death screen, void space logic | All `05-saveload` (9): `SS_SaveManager`, `BPC_DeathHandlingSystem`, `BPC_AltDeathSpaceSystem`, `BPC_PlayerRespawnSystem`, `BPC_PersistentCorpseSystem`, `BPC_PersistentWorldStateRecorder`, `BPC_RunHistoryTracker`, `I_Persistable` | + +--- + +## Polish & State Gating (Build Phases 19-21) + +| Document | Assets | Systems Demonstrated | +|----------|--------|---------------------| +| [polish-loading-credits.md](polish-loading-credits.md) | 6 `BP_TutorialTrigger_*`, `WBP_CreditsScreen`, `WBP_DebugMenu`, `BPC_DevCheatManager` | All `13-polish` (9): `BPC_TutorialSystem`, `BPC_LoadingScreen`, `BPC_AnalyticsTracker`, `BPC_DevCheatManager`, `BPC_ErrorHandler`, `BPC_FPSCounter`, `WBP_CreditsScreen`, `WBP_DebugMenu` | +| [state-gating-examples.md](state-gating-examples.md) | `DA_StateGatingTable_Horror` | `BPC_StateManager` (130), `DA_StateGatingTable` (131) — 15 game-specific gating rules, void space restrictions, horror state logic | --- @@ -34,39 +88,115 @@ Each example follows the same structure: ``` Content/ -├── Framework/ ← Framework systems (read-only, don't modify) +├── Framework/ ← Framework systems (READ-ONLY — NEVER modify) │ ├── Core/ GI_GameFramework, DA_GameTagRegistry, etc. │ ├── Player/ BPC_HealthSystem, BPC_StateManager, etc. │ ├── Inventory/ BP_ItemPickup base, BPC_InventorySystem -│ ├── DataAssets/ DA_ItemData, DA_EquipmentConfig, etc. +│ ├── Weapons/ BP_WeaponBase, BPC_FirearmSystem, etc. +│ ├── UI/ All WBP_* framework widgets +│ ├── AI/ BP_EnemyBase, AI_BaseAgentController, etc. +│ ├── DataAssets/ All DA_* framework Data Assets +│ ├── Input/ All IA_* Input Actions, IMC_* Contexts +│ ├── State/ BPC_StateManager, DA_StateGatingTable, enums +│ ├── Audio/ MS_* MetaSound buses, SS_AudioManager │ └── ... │ -└── Game/ ← YOUR project content (this is what you create) - ├── Items/ DA_ItemData instances (DA_Item_MedKit, DA_Item_Flashlight) - ├── Weapons/ DA_ItemData weapon instances (DA_Item_Pistol) - ├── Pickups/ BP_Pickup_* children (BP_Pickup_MedKit, BP_Pickup_Flashlight) - ├── Actors/ BP_Door_*, BP_Puzzle_* instances - └── ... +└── Game/ ← YOUR PROJECT CONTENT (this is what you create) + ├── Core/ GI_HorrorGame, GM_HorrorGameMode, PC/PS/GS + ├── Characters/ BP_HorrorPlayerCharacter, BP_Enemy_*, BP_NPC_* + ├── Items/ All DA_Item_* Data Assets (13 items) + ├── Pickups/ All BP_Pickup_* actors (18 pickups) + ├── Actors/ BP_Door_*, BP_Puzzle_*, BP_HidingSpot_*, etc. + ├── Weapons/ BP_Pistol_Held, BP_Shotgun_Held, etc. + ├── UI/ All WBP_Game* widgets (8 widgets) + ├── DataAssets/ All DA_* content instances (20+) + ├── AI/ AI controllers, behavior trees, blackboards + ├── Narrative/ DA_Dialogue_*, DA_Objective_*, trigger volumes + ├── Encounters/ BP_EncounterSpawner_* + ├── Atmosphere/ BP_LightEvent_*, BP_ScareEvent_* + ├── Audio/ BP_RoomAudioZone_*, DA_AudioSettings_Horror + ├── Save/ BP_Checkpoint_* instances + ├── Polish/ BP_TutorialTrigger_* + ├── Input/ Game-specific input overrides (if any) + └── Maps/ All 11 .umap levels ``` -> **Rule:** Never modify files in `Content/Framework/`. Create your assets in `Content/Game/` and reference framework systems via interfaces and dispatchers. +> **Rule:** Never modify files in `Content/Framework/`. Create your assets in `Content/Game/` as **children** of framework classes and reference framework systems via interfaces, event dispatchers, and Data Asset references. --- -## Reference — Framework Systems Used by These Examples +## How to Build This Prototype -| Framework System | Asset Path | Used For | -|-----------------|-----------|----------| -| `DA_ItemData` | `Content/Framework/DataAssets/Items/` (C++ class) | Item identity definition | -| `BP_ItemPickup` | `Content/Framework/Inventory/` (base BP) | World pickup actor | -| `BPC_InventorySystem` | C++ component (auto-attach to pawn) | Add/remove/query items | -| `BPC_HealthSystem` | C++ stub → BP child on pawn | Taking/healing damage | -| `BPC_DamageReceptionSystem` | C++ component (auto-attach to pawn) | Damage pipeline | -| `BPC_ConsumableSystem` | BP child on pawn | Using consumable items | -| `BPC_EquipmentSlotSystem` | BP child on pawn | Equipping weapons/tools | -| `BPC_KeyItemSystem` | BP child on pawn | Key item unlock logic | -| `I_Interactable` | C++ interface in `I_InterfaceLibrary.h` | Interaction detection | -| `I_UsableItem` | C++ interface in `I_InterfaceLibrary.h` | Active-use items (flashlight, weapon) | -| `I_Toggleable` | C++ interface in `I_InterfaceLibrary.h` | On/off state (flashlight toggle) | -| `I_Lockable` | C++ interface in `I_InterfaceLibrary.h` | Locked doors/containers | -| `I_Damageable` | C++ interface in `I_InterfaceLibrary.h` | Receiving damage | +### Phase-by-Phase (from GAMEINDEX.md) + +1. **Project Settings** — Enable plugins, register 11 GameplayTag Data Tables, set Asset Manager +2. **Core Classes** — Create 5 game-specific children of framework classes +3. **Maps** — Create splash, menu, and sandbox levels +4. **Player Character** — Build BP_HorrorPlayerCharacter with all 27 components +5. **Items & Pickups** — Create all 13 Data Assets + 18 pickup actors +6. **Doors & Actors** — Create all interaction actors +7. **Weapons** — Create 4 held weapon/tool actors +8. **UI** — Create 8 game-specific widget overrides +9. **Enemies** — Create 3 enemy types with full AI +10. **Narrative** — Create dialogue, objectives, cutscenes, ending system +11. **Atmosphere & Audio** — Create profiles, scare events, room zones +12. **Save System** — Create checkpoints, wire death/respawn/void space +13. **Polish** — Tutorials, loading screen, credits, debug, analytics +14. **State Gating** — Configure game-specific state rules +15. **Full Levels** — Populate all 11 maps with placed assets + +### Each document provides: +1. **What to create** — Exact asset name, parent class, folder location +2. **Blueprint wiring** — Node-by-node logic flows, event graph structure +3. **Variables & configuration** — Every variable with type, default, purpose +4. **Integration points** — How the game asset talks to framework systems +5. **Verification checklist** — How to test it works + +--- + +## Reference — Framework Systems Used + +All 135 framework Blueprint systems are covered by at least one game asset. See [GAMEINDEX.md](GAMEINDEX.md) for the complete framework-to-game mapping. + +| System Category | Framework Systems | Game Assets That Use Them | +|----------------|:---:|---| +| 00-project-setup | 1 | `GI_StarterGameInstance` → `GI_HorrorGame` | +| 01-core | 7 + 11 CSVs | `GI_HorrorGame`, `GM_HorrorGameMode`, `GS_HorrorGameState`, all items/pickups/doors | +| 02-player | 8 | `BP_HorrorPlayerCharacter` (all 8 components) | +| 03-interaction | 8 | All doors, puzzles, hiding spots, usable objects, pickups | +| 04-inventory | 11 | `BP_HorrorPlayerCharacter` (all 11 components), all pickups, containers | +| 05-saveload | 9 | Checkpoints, death system, void space, respawn, persistence | +| 06-ui | 14 | All 8 `WBP_Game*` widget overrides | +| 07-narrative | 11 | Dialogue, objectives, cutscenes, branching choices, endings | +| 08-weapons | 11 | All 4 held weapons, ammo, recoil, reload, hit reactions | +| 09-ai | 9 | 3 enemy types with full AI setup | +| 10-adaptive | 15 | Atmosphere profiles, scare events, lighting, memory drift, pacing, room audio | +| 11-meta | 2 | Achievements, progress stats | +| 12-settings | 2 | Settings menu, accessibility | +| 13-polish | 9 | Tutorials, loading, credits, debug, analytics, error handling | +| 14-data-assets | 16 | All DA_* content instances | +| 15-input | 1 | All input actions + contexts via `SS_EnhancedInputManager` | +| 16-state | 2 | State gating rules, GASP liaison, vital signals | + +--- + +## Notes for Expansion + +This prototype covers the **minimum viable product** to demonstrate all 135 framework systems. For a full game: + +- Replace whitebox meshes with final art assets +- Record professional audio (150+ sound triggers per sound catalog) +- Write complete narrative with branching dialogue for all interactions +- Add 10+ puzzle devices with increasing complexity +- Create 20+ scare events with unique anticipation/payoff pairs +- Implement adaptive difficulty with player metrics feedback loop +- Add 3-5 ending variations +- Localize all text into multiple languages +- Add multiplayer support (server-authoritative replication) +- Create platform-specific builds (PC, Xbox, PS5) + +See each individual document's **"Notes for Expansion"** section for system-specific extension ideas. + +--- + +*README v2.0 — Complete game prototype documentation for Project Void. See [GAMEINDEX.md](GAMEINDEX.md) for the master game structure reference.* diff --git a/docs/game/atmosphere-audio.md b/docs/game/atmosphere-audio.md new file mode 100644 index 0000000..ade42f0 --- /dev/null +++ b/docs/game/atmosphere-audio.md @@ -0,0 +1,367 @@ +# Atmosphere & Audio — Horror Environment Systems + +**Game:** Project Void | **Build Phases:** 14, 16, 17, 18 +**Framework Systems:** 89-101 (adaptive) + 132-135 (MetaSounds audio), 96_BPC_LightEventController, 97_BPC_MemoryDriftSystem, 98_BPC_PacingDirector, 101_BPC_ScareEventSystem + +--- + +## Purpose + +Defines every atmosphere profile, scare event, light event, room audio zone, and adaptive environment system used to create the horror experience. This is where the game's "feel" comes from. + +--- + +## Atmosphere Profiles (DA_AtmosphereProfile) + +Each area has a Data Asset that defines its default audio, lighting, fog, and post-process settings: + +| Profile | Area | Tension | Lighting | Audio Bus | Fog | +|---------|------|:---:|----------|-----------|-----| +| `DA_Atmosphere_WardA_Standard` | WardA | 0.3 | Dim overheads, cool blue | WardA reverb | Light volumetric | +| `DA_Atmosphere_WardB_Dark` | WardB | 0.6 | Flickering, warm amber | WardB echo | Medium volumetric | +| `DA_Atmosphere_Basement_Dark` | Basement | 0.9 | NONE (player-provided) | Heavy reverb, drips | Heavy fog | +| `DA_Atmosphere_VoidSpace` | Void | 1.0 | Inverted (dark fog, white shadows) | Reversed whispers | Black fog | +| `DA_Atmosphere_WardensOffice` | WardensOffice | 0.7 | Single desk lamp, moonlight | Quiet, ticking clock | None | +| `DA_Atmosphere_SafeRoom` | Safe Rooms | 0.0 | Warm, steady lamp light | Soft hum, safe | None | + +### Profile Properties + +``` +DA_AtmosphereProfile Structure: + ├─ ProfileTag (GameplayTag) + ├─ TensionLevel (Float 0.0-1.0) + ├─ LightingSettings (struct) + │ ├─ bOverrideLights (Bool) + │ ├─ LightColorTint (LinearColor) + │ ├─ LightIntensityMultiplier (Float) + │ └─ bEnableFlicker (Bool) + ├─ FogSettings (struct) + │ ├─ FogDensity (Float) + │ ├─ FogColor (LinearColor) + │ └─ bVolumetricFogEnabled (Bool) + ├─ PostProcessSettings + │ ├─ ColorGradingIntensity (Float) + │ ├─ Saturation (Float 0.0-2.0) + │ ├─ Contrast (Float 0.0-2.0) + │ └─ VignetteIntensity (Float) + ├─ AudioSettings + │ ├─ RoomAcousticPreset (DA_RoomAcousticPreset*) + │ ├─ AmbientLoop (SoundBase*) + │ ├─ MusicTrack (SoundBase*) + │ └─ BusVolumeOverrides (Map) + └─ ScareEventPool (Array) +``` + +--- + +## Audio Architecture (MetaSounds) + +### Mix Bus Hierarchy + +``` + ┌─────────────┐ + │ MS_MasterBus │ + └──────┬──────┘ + ┌─────────┬───────┼───────┬─────────┐ + ▼ ▼ ▼ ▼ ▼ + ┌────────┐ ┌──────┐ ┌──────┐ ┌──────┐ ┌────────┐ + │MS_SFXBus│ │MS_Amb│ │MS_Mus│ │MS_Dia│ │(Future)│ + │ │ │Bus │ │Bus │ │Bus │ │ │ + └────────┘ └──────┘ └──────┘ └──────┘ └────────┘ + SFX Ambience Music Dialogue +``` + +### Bus Configuration (DA_AudioSettings_Horror) + +| Bus | Default Volume | Ducking Target | Ducking Amount | Purpose | +|-----|:---:|---|---|---| +| `MS_SFXBus` | 1.0 | MS_DialogueBus | -6dB | Gunshots, footsteps, scares, UI | +| `MS_AmbientBus` | 0.8 | MS_DialogueBus | -3dB | Wind, creaks, drips, whispers | +| `MS_MusicBus` | 0.7 | MS_DialogueBus | -8dB | Tension drones, combat music, theme | +| `MS_DialogueBus` | 1.0 | None | — | VO, subtitles, entity speech | + +### Gameplay Parameters (Pushed via SS_AudioManager) + +``` +SS_AudioManager.SetFloatParameter(ParamName, Value): + + "PlayerHealthPercent" → 0.0–1.0 (BPC_HealthSystem) + └─ MS_SFXBus: heartbeat loudness, breathing intensity + + "StressTier" → 0–4 (BPC_StressSystem) + └─ MS_AmbientBus: whisper density, drone intensity + └─ MS_MusicBus: tension layer volume + + "HeartRate" → 60–180 (BPC_StateManager) + └─ MS_SFXBus: heartbeat speed, pulse intensity + + "FearIntensity" → 0.0–1.0 (GI_HorrorGame) + └─ MS_MusicBus: orchestral intensity, dissonance level + + "TensionLevel" → 0.0–1.0 (BPC_AtmosphereStateController) + └─ MS_AmbientBus: ambient layer crossfade + └─ MS_MusicBus: music layer selection + + "PlayerSpeed" → 0–600 (BPC_MovementStateSystem) + └─ MS_SFXBus: footstep volume and frequency + + "IsHiding" → 0/1 (BPC_HidingSystem) + └─ MS_AmbientBus: muffled external sounds + └─ MS_SFXBus: heartbeat + breath prominence increase + + "VoidActive" → 0/1 (GS_HorrorGameState) + └─ ALL buses: spectral reverb, reversed samples, pitch shift +``` + +--- + +## Room Audio Zones (BP_RoomAudioZone) + +Each zone is a TriggerVolume that switches acoustic preset on overlap: + +| Zone | Preset | Location | +|------|--------|----------| +| `BP_RoomAudioZone_WardA` | `DA_RoomAcoustic_WardA` | All WardA rooms | +| `BP_RoomAudioZone_WardB` | `DA_RoomAcoustic_WardB` | All WardB rooms | +| `BP_RoomAudioZone_Basement` | `DA_RoomAcoustic_Basement` | Basement corridors | +| `BP_RoomAudioZone_Void` | `DA_RoomAcoustic_Void` | Void space | +| `BP_RoomAudioZone_Courtyard` | `DA_RoomAcoustic_Outdoor` | WardB courtyard | +| `BP_RoomAudioZone_SafeRoom` | `DA_RoomAcoustic_SmallRoom` | All safe rooms | + +### Acoustic Presets + +| Preset | Reverb Time | Early Reflections | Occlusion | EQ | +|--------|:---:|------|------|-----| +| `DA_RoomAcoustic_WardA` | 1.2s (medium hall) | Moderate | Low | Flat | +| `DA_RoomAcoustic_WardB` | 2.0s (large echo) | Heavy | Medium | Boost 200Hz | +| `DA_RoomAcoustic_Basement` | 3.5s (cathedral) | Heavy wet | High | Cut highs above 4kHz | +| `DA_RoomAcoustic_Void` | 8.0s (infinite) | Spectral | Full | Reverse delay | +| `DA_RoomAcoustic_Outdoor` | 0.3s (open) | None | None | Wind filter | +| `DA_RoomAcoustic_SmallRoom` | 0.5s (office) | Light | Low | Warm boost | + +--- + +## Scare Event System (BPC_ScareEventSystem) + +### Scare Event Data Assets + +| DA_ScareEvent | Type | Trigger | Anticipation | Payoff | +|--------------|------|---------|-------------|--------| +| `DA_Scare_LockerJumpscare` | Proximity | Player within 200u of locker | 3s (rattling sound) | Locker door BANG open | +| `DA_Scare_MirrorApparition` | Look-at | Player looks at mirror > 2s | 5s (reflection distorts) | Face appears behind player | +| `DA_Scare_WindowFace` | Proximity | Player walks past window | 2s (breath fog on glass) | Hand SLAMS from outside | +| `DA_Scare_VoidWhisper` | Random timer | 5-15 min intervals | None (sudden) | Loud whisper in player's ear | +| `DA_Scare_CeilingThump` | Proximity | Player in basement corridor | 4s (distant footsteps above) | Loud THUMP directly above | +| `DA_Scare_BreathingBehind` | Stress trigger | Stress > 70 | 6s (breathing fades in behind) | Nothing there — just audio | + +### Scare Event Flow + +``` +BPC_ScareEventSystem.TriggerScare(DA_ScareEvent) + │ + ├─ [Cooldown Check] + │ └─ Was a scare triggered in last 15 seconds? → Return (no spam) + │ + ├─ [Eligibility Check] + │ ├─ Player state allows scare? (Not dead, not cutscene) + │ ├─ Accessibility: "reduce_jumpscares" setting? → use soft variant + │ └─ Difficulty: scale intensity by difficulty scalar + │ + ├─ [Anticipation Phase] (duration from DA_ScareEvent.AnticipationTime) + │ ├─ Audio: fade in tension sound (creaking, breathing, footsteps) + │ ├─ BPC_StressSystem.AddStress(+5) during anticipation + │ ├─ WBP_ScreenEffectController: subtle vignette darkening + │ └─ (Player is tense — they know something is coming) + │ + ├─ [Payoff Phase] (instant) + │ ├─ Audio: scare sting (loud sound per scare type) + │ ├─ Visual: screen shake, flash, or particle burst + │ ├─ BPC_StressSystem.AddStress(+15) — spike + │ ├─ SS_AudioManager.PlaySFX(ScareSting) + │ └─ BPC_PlayerMetricsTracker.RecordScare(ScareType) + │ + ├─ [Recovery Phase] (3-5 seconds) + │ ├─ Audio: fade scare sting, return to ambient + │ ├─ Visual: vignette fades back + │ ├─ BPC_StressSystem.StartDecay() (5 seconds of rapid decay) + │ └─ WBP_HUDController: subtle "heartbeat" indicator pulse + │ + └─ Record scare in PS_HorrorPlayerState.FearHistory +``` + +--- + +## Light Events (BPC_LightEventController) + +| Light Event | Trigger | Effect | Duration | +|------------|---------|--------|:---:| +| `Flicker` | Random (5-15 min intervals) | All lights flicker 3-5 times | 1-2s | +| `BulbExplosion` | Scripted (basement morgue) | Light bulb bursts → sparks → darkness | Permanent | +| `EmergencyRed` | Void shift active | All lights tint red, strobe effect | 30s | +| `ShadowPass` | Random (3-8 min intervals) | Shadow passes across lights (like something walked past) | 0.5s | +| `TotalBlackout` | Scripted (basement entry) | All lights off. Flashlight-only. | Until generator puzzle solved | +| `GhostLight` | Rare event | Blue-white orb floats down hallway → vanishes | 5s | + +### Light Event Flow + +``` +BPC_LightEventController.TriggerLightEvent(LightEventTag) + │ + ├─ [Flicker] + │ ├─ For each PointLight in tagged group: + │ │ └─ Random Delay(0.1-0.4s) → ToggleVisibility × 4 iterations + │ └─ SS_AudioManager.PlaySFX("light_flicker") + │ + ├─ [BulbExplosion] + │ ├─ Target Light → SetIntensity(0) + Play spark particle + │ ├─ SS_AudioManager.PlaySFX("glass_shatter") + │ └─ Room now darker → update atmosphere profile + │ + ├─ [TotalBlackout] + │ ├─ All lights → SetIntensity(0) over 1s (smooth dim) + │ ├─ SS_AudioManager.CrossfadeAmbient("basement_dark") + │ └─ BPC_StressSystem.StartContinuousStress(+2/sec, "Darkness") + │ + └─ [ShadowPass] + ├─ Spawn invisible actor moving across lights + └─ Light shafts briefly occluded +``` + +--- + +## Memory Drift System (BPC_MemoryDriftSystem) + +Hallucinations triggered by high stress. Intensity scaled by stress tier: + +``` +BPC_MemoryDriftSystem.SetIntensity(Tier: 0-4) + │ + ├─ Tier 0 (Calm): No effects + │ + ├─ Tier 1 (Uneasy): Very rare whisper audio cues + │ + ├─ Tier 2 (Disturbed): Peripheral vision flickers + │ Occasional false sound cues + │ Text in journal shifts subtly + │ + ├─ Tier 3 (Breaking): Tunnel vision + desaturation + │ False enemy movement (corner of eye) + │ Dialogue lines sometimes echo/repeat + │ Environment "breathes" (wall textures warp) + │ + └─ Tier 4 (Catatonic): Near-black peripheral vision + False enemies appear (visual only) + Player name whispered in audio + Doors appear where none exist (can't walk through) + Text unreadable (shifts rapidly) +``` + +--- + +## Pacing Director (BPC_PacingDirector) + +Manages the rhythm of horror: exploration → tension → encounter → recovery. + +``` +Intensity Bands: + ├─ Band 0: RECOVERY (Safe rooms, after encounter ends) + │ └─ Atmosphere: Calm, music: soft, enemies: none + │ + ├─ Band 1: EXPLORATION (Default, new areas) + │ └─ Atmosphere: Light tension, music: low drone, enemies: patrol only + │ + ├─ Band 2: TENSION (Near objectives, dark areas) + │ └─ Atmosphere: Moderate tension, music: building, enemies: searching + │ + ├─ Band 3: ENCOUNTER (Enemy sighted, combat) + │ └─ Atmosphere: High tension, music: combat layer, enemies: hostile + │ + └─ Band 4: CLIMAX (Void shift, Shade pursuit, ending) + └─ Atmosphere: Maximum, music: full orchestral, enemies: relentless + +Transition Triggers: + ├─ Recovery → Exploration: Player leaves safe room + ├─ Exploration → Tension: Player enters dark area OR finds key item + ├─ Tension → Encounter: Enemy spots player + ├─ Encounter → Recovery: Enemy killed/evaded + 10 seconds passed + └─ Recovery → Climax: Entering void space or ending sequence +``` + +--- + +## Adaptive Environment Director (BPC_AdaptiveEnvironmentDirector) + +Handles room mutations and dynamic level changes: + +``` +TriggerRoomMutation(MutationTag) + │ + ├─ "VoidShift" → WardB Morgue + │ ├─ Walls: temporary overlay material (bleeding concrete) + │ ├─ Corridors: loop — walking forward returns to same room + │ ├─ Doors: random open/close cycle + │ ├─ Audio: crossfade to void ambience + │ └─ Duration: 30 seconds, then revert + │ + ├─ "BasementMaze" → Basement + │ ├─ Doors randomize their locked/unlocked state every 60s + │ └─ Player must navigate changing maze + │ + └─ "VoidSpaces" → Void level + ├─ Rooms rearrange via data layers (swap layout presets) + ├─ Triggered on player entering new room + └─ Creates non-Euclidean navigation +``` + +--- + +## Blueprint Wiring Checklist + +### Atmosphere +- [ ] Create all 6 `DA_AtmosphereProfile` Data Assets +- [ ] Wire `BPC_AtmosphereStateController.LoadProfile()` to apply on level load +- [ ] Add `BP_AtmosphereController_WardA` actor to WardA level + +### Audio +- [ ] Create `DA_AudioSettings_Horror` — bus volumes, ducking rules +- [ ] Create all 6 `DA_RoomAcousticPreset` Data Assets +- [ ] Create `BP_RoomAudioZone_*` TriggerVolumes for each area +- [ ] Wire gameplay parameters: BPC_HealthSystem → "PlayerHealthPercent", etc. +- [ ] Set up `SS_AudioManager` with 4 MetaSound buses + +### Scares +- [ ] Create 6 `DA_ScareEvent` Data Assets +- [ ] Wire `BPC_ScareEventSystem.TriggerScare()` with cooldown + eligibility checks +- [ ] Create `BP_ScareEvent_LockerBang` and `BP_ScareEvent_Mirror` actors + +### Lights +- [ ] Wire `BPC_LightEventController` with 6 light event types +- [ ] Create `BP_LightEvent_Flicker` actor for placed flicker points + +### Memory Drift +- [ ] Wire `BPC_MemoryDriftSystem.SetIntensity()` to stress tier changes +- [ ] Add visual effects for each tier (vignette, texture warp, false actors) + +### Pacing +- [ ] Wire `BPC_PacingDirector` with 5 intensity bands +- [ ] Add transitions: area entry/exit, enemy detection, safe room entry + +### Adaptive Environment +- [ ] Wire `BPC_AdaptiveEnvironmentDirector` for void shift + basement maze +- [ ] Create data layers for swappable room layouts in void space + +--- + +## Notes for Expansion + +- Add **dynamic music system**: adaptive music that layers stems based on tension/pacing band +- Add **environmental storytelling through audio**: ghostly voices re-enacting past events +- Add **weather system**: external rain sounds through windows (only in courtyard-facing rooms) +- Add **binaural audio** (3D sound) support for headphones — whispers sound like they're behind you +- Add **haptic profile** (DA_HapticProfile) for DualSense: heartbeat pulse, gunshot kick, floor rumble +- Add **room mutation presets** as `DA_RoomMutation` Data Assets for designers to configure +- Consider **performance scaling** of audio: fewer simultaneous voices on lower-end hardware + +--- + +*Atmosphere & Audio for Project Void. See [GAMEINDEX.md](GAMEINDEX.md) for full game structure. See [metasounds-audio-system.md](../architecture/metasounds-audio-system.md) for audio architecture. See [sound-catalog.md](../architecture/sound-catalog.md) for full sound trigger list.* diff --git a/docs/game/core-controller-state.md b/docs/game/core-controller-state.md new file mode 100644 index 0000000..7f9731b --- /dev/null +++ b/docs/game/core-controller-state.md @@ -0,0 +1,308 @@ +# Game Core — PC_HorrorController, PS_HorrorPlayerState, GS_HorrorGameState + +**Game:** Project Void | **Build Phase:** 1 + +--- + +This document covers the 3 remaining Core class children: + +| Asset | Parent | Purpose | +|-------|--------|---------| +| `PC_HorrorController` | `PC_CoreController` (or `PlayerController`) | Horror-specific input routing, camera, UI injection | +| `PS_HorrorPlayerState` | `PS_CorePlayerState` (or `PlayerState`) | Horror player data: fear history, deaths, sanity | +| `GS_HorrorGameState` | `GS_CoreGameState` (or `GameStateBase`) | Shared session data: global flags, void state, timer | + +--- + +## PC_HorrorController — Player Controller + +**Asset Path:** `Content/Game/Core/PC_HorrorController.uasset` + +### Purpose + +Routes player input through `SS_EnhancedInputManager`, manages camera shake, hit feedback, and UI injection for horror-specific screen effects. + +### Creation Steps + +#### Step 1 — Create Blueprint + +``` +Content Browser → Game/Core/ + Right-click → Blueprint Class + Parent Class: PlayerController (or PC_CoreController if it exists in framework) + Name: PC_HorrorController +``` + +#### Step 2 — Variables + +| Variable | Type | Default | Category | Purpose | +|----------|------|---------|----------|---------| +| `bGameInputEnabled` | Boolean | `true` | `Input` | Master input toggle (off during cutscenes/death) | +| `CameraShakeIntensity` | Float | `1.0` | `Camera` | Global scalar for camera shake effects | +| `bHeartbeatEnabled` | Boolean | `true` | `UI` | Toggle diegetic heartbeat indicator | + +#### Step 3 — Wire Event BeginPlay + +``` +Event BeginPlay + │ + ├─ Set Input Mode Game Only (mouse captured, no cursor) + ├─ Set Show Mouse Cursor → false + │ + ├─ [Get Enhanced Input Subsystem] + │ └─ Get Engine Subsystem → UEnhancedInputLocalPlayerSubsystem + │ + ├─ [Bind to Game Phase Changes] + │ ├─ Get GI_HorrorGame → Bind to OnGamePhaseChanged + │ │ └─ On Paused/Cutscene/DeathLoop → Set bGameInputEnabled = false + │ │ └─ On InGame → Set bGameInputEnabled = true + │ │ + │ └─ SS_UIManager → Bind to OnInputModeChanged + │ └─ Update input mode + cursor visibility + │ + └─ [Start Heartbeat Effect] + └─ (Timer: every 0.5s) Get BPC_StateManager → GetHeartRate() + ├─ Normal (60-80) → slight controller pulse + ├─ Elevated (80-110) → stronger pulse + └─ Critical (110+) → rapid, intense pulse + screen edge red +``` + +#### Step 4 — Override: Setup Input Component + +``` +Setup Input Component + │ + ├─ Parent: Setup Input Component + │ + └─ (Input actions are mapped via SS_EnhancedInputManager and IMC_ contexts + — no manual binding needed here. The subsystem handles context pushes.) +``` + +#### Step 5 — Functions + +| Function | Inputs | Outputs | Description | +|----------|--------|---------|-------------| +| `PlayCameraShake` | ShakeClass, Scale: Float | — | `ClientStartCameraShake(ShakeClass, Scale × CameraShakeIntensity)` | +| `SetGameInputEnabled` | bEnabled: Boolean | — | Master toggle — disables all input processing | +| `OpenPauseMenu` | — | — | `SS_UIManager.PushMenu(PauseMenu)` — bound to IA_PauseMenu | +| `GetInteractionSystem` | — | `BPC_InteractionDetector*` | Quick access to interaction component on pawn | + +### Communications With + +| Target | Method | Why | +|--------|--------|-----| +| `SS_EnhancedInputManager` | Direct | Context management, input state queries | +| `SS_UIManager` | Direct + Dispatcher | Input mode coordination | +| `GI_HorrorGame` | Dispatcher | Phase change response | +| `BPC_StateManager` | Direct (on pawn) | Heart rate for haptic feedback | +| `BP_HorrorPlayerCharacter` | Owned Pawn | Camera shake routing | + +--- + +## PS_HorrorPlayerState — Player State + +**Asset Path:** `Content/Game/Core/PS_HorrorPlayerState.uasset` + +### Purpose + +Tracks player-specific data that persists across level transitions and death/respawn. Custom data: fear history, death count per chapter, sanity tier. + +### Creation Steps + +#### Step 1 — Create Blueprint + +``` +Content Browser → Game/Core/ + Right-click → Blueprint Class + Parent Class: PlayerState (or PS_CorePlayerState if it exists in framework) + Name: PS_HorrorPlayerState +``` + +#### Step 2 — Variables + +| Variable | Type | Default | Category | Purpose | +|----------|------|---------|----------|---------| +| `SanityTier` | Integer | `100` | `Player` | 100=Calm, 75=Uneasy, 50=Disturbed, 25=Breaking, 0=Catatonic | +| `FearHistory` | Array\ | Empty | `Player` | Tracks fear triggers experienced (for ending calculation) | +| `DeathsThisChapter` | Integer | `0` | `Player` | Resets per chapter; used for difficulty scaling | +| `TotalCollectiblesFound` | Integer | `0` | `Player` | Across all levels | +| `PlaystyleTag` | GameplayTag | `Framework.Playstyle.Balanced` | `Player` | Aggressive / Stealthy / Explorer / Balanced | +| `PlayerName` | Text | "Dr. Eliza Vance" | `Player` | Display name (set by GM on spawn) | + +#### Step 3 — Functions + +| Function | Inputs | Outputs | Description | +|----------|--------|---------|-------------| +| `RecordFearTrigger` | TriggerTag: GameplayTag | — | Adds tag to FearHistory, notifies BPC_EndingAccumulator | +| `SetSanityTier` | NewTier: Int | — | Updates sanity; clamps 0–100; fires OnSanityChanged dispatcher | +| `IncrementDeaths` | — | — | DeathsThisChapter++, fires OnDeathCountChanged | +| `GetPlaystyle` | — | GameplayTag | Returns PlaystyleTag | +| `GetSanityPercentage` | — | Float | Returns SanityTier as 0.0–1.0 float | + +### Replication Notes (Multiplayer) + +All variables should be marked `Replicated` with `ReplicatedUsing = OnRep_`. + +--- + +## GS_HorrorGameState — Game State + +**Asset Path:** `Content/Game/Core/GS_HorrorGameState.uasset` + +### Purpose + +Shared session state visible to all players (server + all clients in multiplayer). In single-player, serves as the authoritative session data store. + +### Creation Steps + +#### Step 1 — Create Blueprint + +``` +Content Browser → Game/Core/ + Right-click → Blueprint Class + Parent Class: GameStateBase (or GS_CoreGameState if it exists in framework) + Name: GS_HorrorGameState +``` + +#### Step 2 — Variables + +| Variable | Type | Default | Category | Purpose | +|----------|------|---------|----------|---------| +| `ActiveChapterTag` | GameplayTag | — | `Session` | Current chapter/level identifier | +| `bIsVoidActive` | Boolean | `false` | `Session` | True when any player is in void space | +| `GlobalFearLevel` | Float | `0.0` | `Session` | 0.0–1.0 — ambient fear level affecting all systems | +| `SessionPlaytime` | Float | `0.0` | `Session` | Accumulated playtime in seconds | +| `bEndingSequenceActive` | Boolean | `false` | `Session` | True during ending cutscene (blocks all gameplay) | +| `GlobalFlags` | Map\ | Empty | `Session` | Chapter-complete flags, puzzle-solved flags, etc. | + +#### Step 3 — Event Graph + +``` +Event BeginPlay + │ + ├─ Set GlobalFearLevel = 0.0 + │ + └─ [Start Playtime Timer] + └─ Set Timer (1.0s, Looping) → SessionPlaytime += 1.0 + └─ Only increment when GamePhase == InGame + +Event Tick (or Timer) + │ + ├─ Get GI_HorrorGame → CurrentGamePhase + │ └─ If InGame: SessionPlaytime += DeltaTime + │ └─ Else: don't accumulate + │ + └─ [Fear Decay] + ├─ GlobalFearLevel > 0.0? + │ └─ True: GlobalFearLevel -= 0.001 per second (slow decay) + │ └─ Clamp to 0.0 minimum + └─ Broadcast OnGlobalFearChanged(GlobalFearLevel) +``` + +#### Step 4 — Functions + +| Function | Inputs | Outputs | Description | +|----------|--------|---------|-------------| +| `SetGlobalFlag` | FlagTag: GameplayTag, Value: Boolean | — | Sets session flag + broadcasts OnFlagChanged | +| `GetGlobalFlag` | FlagTag: GameplayTag | Boolean | Returns flag value (false if not found) | +| `AddFearLevel` | Amount: Float | — | GlobalFearLevel += Amount, clamped 0.0–1.0 | +| `SetVoidActive` | bActive: Boolean | — | Sets bIsVoidActive, broadcasts OnVoidStateChanged | +| `GetSessionTimeFormatted` | — | Text | Returns "HH:MM:SS" formatted playtime | +| `ResetSession` | — | — | Resets all variables (called by New Game) | + +#### Step 5 — Event Dispatchers + +| Dispatcher | Parameters | Fired When | +|------------|-----------|------------| +| `OnFlagChanged` | FlagTag: GameplayTag, Value: Boolean | Any global flag changes | +| `OnGlobalFearChanged` | FearLevel: Float | GlobalFearLevel updates | +| `OnVoidStateChanged` | bIsVoidActive: Boolean | Void space active/inactive | +| `OnChapterChanged` | ChapterTag: GameplayTag | Active chapter changes | +| `OnSessionReset` | — | Session data wiped (New Game) | + +--- + +## Combined Wiring — All Core Classes Together + +``` +[GI_HorrorGame.Init] + │ + ├─ Initialize subsystems (Audio, Settings, Input) + └─ OpenLevel → L_SplashScreen + │ + └─ L_SplashScreen → auto-advance → L_MainMenu + │ + └─ [Player clicks New Game] + │ + └─ GM_HorrorGameMode starts new session + │ + ├─ Spawn PS_HorrorPlayerState (fresh data) + ├─ Spawn GS_HorrorGameState (fresh session) + │ + ├─ Spawn BP_HorrorPlayerCharacter (pawn) + │ └─ Auto-added components: + │ ├─ BPC_HealthSystem (100 HP) + │ ├─ BPC_StaminaSystem (100 stam) + │ ├─ BPC_StressSystem (0 stress) + │ ├─ BPC_MovementStateSystem + │ ├─ BPC_HidingSystem + │ ├─ BPC_EmbodimentSystem + │ ├─ BPC_CameraStateLayer + │ ├─ BPC_PlayerMetricsTracker + │ ├─ BPC_InteractionDetector + │ ├─ BPC_InventorySystem + │ ├─ BPC_EquipmentSlotSystem + │ ├─ BPC_ActiveItemSystem + │ ├─ BPC_ConsumableSystem + │ ├─ BPC_StateManager + │ └─ [others as needed] + │ + ├─ Spawn PC_HorrorController + │ ├─ Bind input contexts + │ └─ Set Input Mode Game Only + │ + └─ Create WBP_GameHUDController + ├─ WBP_DiegeticHUDFrame (health, stress, stamina) + ├─ WBP_InteractionPromptDisplay + ├─ WBP_ObjectiveDisplay + ├─ WBP_NotificationToast + ├─ WBP_ScreenEffectController + └─ WBP_AccessibilityUI (subtitles) +``` + +--- + +## Blueprint Wiring Checklist + +### PC_HorrorController +- [ ] Create BP child of PlayerController named `PC_HorrorController` +- [ ] Add 3 variables (bGameInputEnabled, CameraShakeIntensity, bHeartbeatEnabled) +- [ ] Wire `Event BeginPlay` — input mode, phase bindings, heartbeat timer +- [ ] Add `PlayCameraShake`, `SetGameInputEnabled`, `OpenPauseMenu`, `GetInteractionSystem` + +### PS_HorrorPlayerState +- [ ] Create BP child of PlayerState named `PS_HorrorPlayerState` +- [ ] Add 5 variables (SanityTier, FearHistory, DeathsThisChapter, TotalCollectiblesFound, PlaystyleTag) +- [ ] Add `RecordFearTrigger`, `SetSanityTier`, `IncrementDeaths`, `GetPlaystyle`, `GetSanityPercentage` +- [ ] Mark variables Replicated for multiplayer + +### GS_HorrorGameState +- [ ] Create BP child of GameStateBase named `GS_HorrorGameState` +- [ ] Add 6 variables (ActiveChapterTag, bIsVoidActive, GlobalFearLevel, SessionPlaytime, bEndingSequenceActive, GlobalFlags) +- [ ] Add 4 event dispatchers (OnFlagChanged, OnGlobalFearChanged, OnVoidStateChanged, OnChapterChanged, OnSessionReset) +- [ ] Wire `Event BeginPlay` — fear decay timer +- [ ] Add `SetGlobalFlag`, `GetGlobalFlag`, `AddFearLevel`, `SetVoidActive`, `GetSessionTimeFormatted`, `ResetSession` + +--- + +## Notes for Expansion + +- The `PS_HorrorPlayerState.SanityTier` maps directly to `BPC_StressSystem` — they should sync via dispatcher +- `GS_HorrorGameState.GlobalFearLevel` could drive `BPC_PacingDirector` to spawn more/less enemies +- In a multiplayer game, `GS_HorrorGameState` is the server-authoritative source for all session data +- Consider adding `PS_HorrorPlayerState.FearHistory` as a debug visualization in `WBP_DebugMenu` +- The heartbeat haptic effect in `PC_HorrorController` requires gamepad support — add a fallback for KB+M + +--- + +*Core controller + state classes for Project Void. See [GAMEINDEX.md](GAMEINDEX.md) for full game structure. See [05_GM_CoreGameMode.md](../blueprints/01-core/05_GM_CoreGameMode.md) and [06_GS_CoreGameState.md](../blueprints/01-core/06_GS_CoreGameState.md) for parent specs.* diff --git a/docs/game/core-gameinstance.md b/docs/game/core-gameinstance.md new file mode 100644 index 0000000..74941fc --- /dev/null +++ b/docs/game/core-gameinstance.md @@ -0,0 +1,188 @@ +# Game Core — GI_HorrorGame (Game Instance) + +**Game:** Project Void | **Asset:** `GI_HorrorGame` | **Parent:** `GI_GameFramework` +**Asset Path:** `Content/Game/Core/GI_HorrorGame.uasset` +**Build Phase:** 1 | **Framework System:** 04_GI_GameFramework + +--- + +## Purpose + +The game-specific Game Instance. Inherits all kernel functionality from `GI_GameFramework` (game phase state machine, session flags, save slot management, service resolution, platform init). This BP child adds game-specific bootstrap configuration and horror-game startup sequences. + +--- + +## Dependencies + +| System | Type | Why | +|--------|------|-----| +| `GI_GameFramework` (04) | Parent class | Inherits all kernel systems | +| `GM_HorrorGameMode` | Referenced | Default game mode for all levels | +| `DA_GameTagRegistry` (01) | Configured | Tag registry assigned in Class Defaults | +| All `SS_` Subsystems | Owned | Initialized by parent on boot | +| `DA_AudioSettings_Horror` (134) | Configured | Audio mix defaults for horror game | + +--- + +## Creation Steps + +### Step 1 — Create Blueprint Child + +``` +Content Browser → Game/Core/ + Right-click → Blueprint Class + Parent Class: GI_GameFramework (from Framework/Core/) + Name: GI_HorrorGame +``` + +### Step 2 — Configure Class Defaults + +Open `GI_HorrorGame` → Class Defaults: + +| Property | Value | Notes | +|----------|-------|-------| +| `TagRegistry` | `DA_GameTagRegistry` (Framework/Core/) | Critical — validates all gameplay tags on boot | +| `ActiveSaveSlotIndex` | `0` | Default save slot | +| `PlatformType` | `Generic` | Change to Steam/PlayStation/Xbox per build | +| `bFirstLaunch` | `true` | Triggers intro flow on first-ever boot | + +### Step 3 — Add Game-Specific Variables + +| Variable | Type | Default | Category | Purpose | +|----------|------|---------|----------|---------| +| `bIntroPlayed` | Boolean | `false` | `Game` | Tracks if opening cutscene has been played | +| `TotalDeaths` | Integer | `0` | `Game` | Global death counter (persists across runs) | +| `FearIntensityMultiplier` | Float | `1.0` | `Game` | Global scalar for scare/fear intensity | +| `ActiveEndingTag` | GameplayTag | — | `Game` | Tracks which ending path player is on | + +### Step 4 — Wire Event Init (Game-Specific Override) + +``` +[Event Init] (inherited from GI_GameFramework — call parent first) + │ + ├─ Parent: Event Init + │ + ├─ [Bootstrap Game Services] + │ │ + │ ├─ GetSubsystem(SS_AudioManager) → Initialize (DA_AudioSettings_Horror) + │ │ └─ Sets horror-specific mix bus levels (quieter music, louder SFX) + │ │ + │ ├─ GetSubsystem(SS_SettingsSystem) → Apply Settings + │ │ └─ Loads persisted audio/video/control settings + │ │ + │ └─ GetSubsystem(SS_EnhancedInputManager) → Initialize (DA_InputMapping_Horror_PC) + │ └─ Load horror-specific keybindings + │ + ├─ [Check First Launch] + │ │ + │ ├─ bFirstLaunch? → Branch + │ │ ├─ True: + │ │ │ ├─ SetSessionFlag("HasSeenIntro", false) + │ │ │ └─ (Optional) Reset achievement progress for fresh start + │ │ └─ False: + │ │ └─ SetSessionFlag("HasSeenIntro", true) + │ │ + │ └─ Set bFirstLaunch = false (won't trigger again this session) + │ + ├─ [Route to First Scene] + │ │ + │ ├─ Set Game Phase → Loading + │ ├─ OpenLevel → "L_SplashScreen" + │ └─ (Splash auto-advances to L_MainMenu after sequence) + │ + └─ Broadcast OnFrameworkReady +``` + +### Step 5 — Add Game-Specific Functions + +#### `SetFearIntensity(Float Multiplier)` +``` +Input: Multiplier (Float) +Output: none +Flow: + ├─ FearIntensityMultiplier = Multiplier + ├─ GetSubsystem(SS_AudioManager) → SetFloatParameter("FearIntensity", Multiplier) + └─ Optionally notify BPC_PacingDirector of new intensity +``` + +#### `GetEndingAccumulator()` → BPC_EndingAccumulator +``` +Output: BPC_EndingAccumulator Reference +Flow: + ├─ Get Player Pawn → GetComponentByClass(BPC_EndingAccumulator) + └─ Return reference (null if player isn't spawned yet — only valid InGame) +``` + +#### `QuitToDesktop()` +``` +Flow: + ├─ GetSubsystem(SS_SaveManager) → QuickSave (if in-game) + ├─ SetSessionFlag("CleanQuit", true) + └─ UKismetSystemLibrary.QuitGame(GetWorld(), GetPlayerController(), Quit) +``` + +--- + +## Key Differences from `GI_GameFramework` Parent + +| Aspect | GI_GameFramework (Framework) | GI_HorrorGame (Game) | +|--------|---------------------------|---------------------| +| Tag Registry | Not set (abstract) | `DA_GameTagRegistry` assigned | +| Audio Settings | None | `DA_AudioSettings_Horror` | +| Input Profile | None | `DA_InputMapping_Horror_PC` | +| First Route | Generic | `L_SplashScreen` → `L_MainMenu` | +| Game Variables | Session flags only | `TotalDeaths`, `FearIntensityMultiplier`, `ActiveEndingTag` | + +--- + +## Blueprint Wiring Checklist + +- [ ] Create BP child of `GI_GameFramework` named `GI_HorrorGame` in `Game/Core/` +- [ ] Set `TagRegistry` → `DA_GameTagRegistry` in Class Defaults +- [ ] Add 4 game-specific variables (bIntroPlayed, TotalDeaths, FearIntensityMultiplier, ActiveEndingTag) +- [ ] Override `Event Init` → call Parent → bootstrap audio, settings, input → route to splash +- [ ] Add `SetFearIntensity` function +- [ ] Add `GetEndingAccumulator` function +- [ ] Add `QuitToDesktop` function +- [ ] Set `GI_HorrorGame` as Default Game Instance in Project Settings → Maps & Modes + +--- + +## Project Settings Configuration + +``` +Edit → Project Settings → Maps & Modes + Default Game Instance: GI_HorrorGame (NOT GI_GameFramework) + +Edit → Project Settings → Gameplay Tags + Add all 11 DT_Tags_*.csv Data Tables to Gameplay Tag Table List + (See project-setup-migration.md section 2.2) +``` + +--- + +## Communications With + +| Target System | Method | Why | +|--------------|--------|-----| +| `GM_HorrorGameMode` | Direct (spawned by engine) | Game rules | +| `SS_AudioManager` | `GetSubsystem` | Audio bus config on boot | +| `SS_SettingsSystem` | `GetSubsystem` | Load/apply settings on boot | +| `SS_EnhancedInputManager` | `GetSubsystem` | Load input profile on boot | +| `SS_SaveManager` | `GetSubsystem` | Auto-save on quit | +| `BPC_PacingDirector` | Dispatcher | Fear intensity changes | +| `BPC_EndingAccumulator` | Interface | Ending path tracking | + +--- + +## Notes for Expansion + +- To add **Steam integration**: set `PlatformType = Steam`, add Steam subsystem init in `Event Init` +- To add **Epic Online Services**: create a `SS_OnlineManager` subsystem, init in `Event Init` +- To support **DLC chapters**: add a `LoadedChapters` map variable, query on level load +- The `FearIntensityMultiplier` could drive a **global difficulty scaler** accessible to all systems +- Consider adding an `OnGameBootComplete` dispatcher for systems that need to wait for full init + +--- + +*GI_HorrorGame — Core game instance for Project Void. See [GAMEINDEX.md](GAMEINDEX.md) for full game structure. See [04_GI_GameFramework.md](../blueprints/01-core/04_GI_GameFramework.md) for parent class spec.* diff --git a/docs/game/core-gamemode.md b/docs/game/core-gamemode.md new file mode 100644 index 0000000..1738bb4 --- /dev/null +++ b/docs/game/core-gamemode.md @@ -0,0 +1,275 @@ +# Game Core — GM_HorrorGameMode (Game Mode) + +**Game:** Project Void | **Asset:** `GM_HorrorGameMode` | **Parent:** `GM_CoreGameMode` +**Asset Path:** `Content/Game/Core/GM_HorrorGameMode.uasset` +**Build Phase:** 1 | **Framework System:** 05_GM_CoreGameMode + +--- + +## Purpose + +The game-specific Game Mode. Inherits all core rules from `GM_CoreGameMode` (player spawn, chapter transitions, death handling, pause control). This BP child configures the horror-specific pawn, controller, state classes, and adds game-over routing, void-space transitions, and ending triggers. + +--- + +## Dependencies + +| System | Type | Why | +|--------|------|-----| +| `GM_CoreGameMode` (05) | Parent class | Inherits TransitionToChapter, HandlePlayerDead, etc. | +| `BP_HorrorPlayerCharacter` | Spawned | Default pawn class | +| `PC_HorrorController` | Spawned | Default player controller | +| `PS_HorrorPlayerState` | Managed | Default player state | +| `GS_HorrorGameState` | Managed | Default game state | +| `WBP_GameHUDController` | Created | Default HUD class | +| `BPC_DeathHandlingSystem` (39) | Dispatcher | Receive death events | +| `BPC_EndingAccumulator` (68) | Dispatcher | Trigger ending evaluation | +| `BPC_NarrativeStateSystem` (58) | Interface | Chapter flag coordination | +| `SS_SaveManager` (35) | Direct | Checkpoint data on level transitions | + +--- + +## Creation Steps + +### Step 1 — Create Blueprint Child + +``` +Content Browser → Game/Core/ + Right-click → Blueprint Class + Parent Class: GM_CoreGameMode (from Framework/Core/) + Name: GM_HorrorGameMode +``` + +### Step 2 — Configure Class Defaults + +Open `GM_HorrorGameMode` → Class Defaults → **Classes** section: + +| Property | Value | Notes | +|----------|-------|-------| +| `Default Pawn Class` | `BP_HorrorPlayerCharacter` | GASP-based horror player | +| `Player Controller Class` | `PC_HorrorController` | Custom input routing | +| `Player State Class` | `PS_HorrorPlayerState` | Player data | +| `Game State Class` | `GS_HorrorGameState` | Shared session data | +| `HUD Class` | `WBP_GameHUDController` | Horror-themed HUD | +| `Default Player Name` | "Dr. Eliza Vance" | In-game display name | +| `bPauseAllowed` | `true` (default) | Set false during scripted sequences | + +### Step 3 — Add Game-Specific Variables + +| Variable | Type | Default | Category | Purpose | +|----------|------|---------|----------|---------| +| `bVoidSpaceActive` | Boolean | `false` | `Game` | True when player is in void/alt-death space | +| `bEndingTriggered` | Boolean | `false` | `Game` | Prevents multiple ending triggers | +| `GameDifficulty` | Float | `1.0` | `Game` | 0.5–2.0 scalar for all difficulty systems | +| `HorrorLevelNames` | Map (GameplayTag → Name) | — | `Levels` | ChapterTag → Level name for transitions | + +### Step 4 — Populate HorrorLevelNames Map + +| Key (GameplayTag) | Value (Level Name) | Description | +|-------------------|-------------------|-------------| +| `Chapter.Entry` | `L_Asylum_Entry` | Tutorial zone | +| `Chapter.WardA` | `L_Asylum_WardA` | First major area | +| `Chapter.WardB` | `L_Asylum_WardB` | Second major area | +| `Chapter.Basement` | `L_Asylum_Basement` | Dark horror area | +| `Chapter.VoidSpace` | `L_Asylum_VoidSpace` | Reality-shift zone | +| `Chapter.WardensOffice` | `L_Asylum_WardensOffice` | Climax | +| `Chapter.Ending` | `L_Ending_Truth` | Final cutscene | +| `Chapter.Credits` | `L_Credits` | Credits roll | + +### Step 5 — Wire Event Graph + +#### Override: Event OnPostLogin + +``` +Event OnPostLogin (NewPlayer: PlayerController*) + │ + ├─ Parent: Event OnPostLogin (call parent first) + │ + ├─ Get GI_HorrorGame → GetSessionFlag("HasSeenIntro")? + │ ├─ False → (first time in level) + │ │ ├─ Set bPauseAllowed = false + │ │ ├─ BPC_CutsceneBridge → PlayCutscene("Opening") + │ │ │ └─ Player wakes in padded cell (3-5 second sequence) + │ │ ├─ GI_HorrorGame → SetSessionFlag("HasSeenIntro", true) + │ │ ├─ BPC_TutorialSystem → StartTutorial("Controls") + │ │ └─ Set bPauseAllowed = true (after cutscene) + │ │ + │ └─ True → (returning from death, reload, or later visit) + │ └─ BPC_PlayerRespawnSystem → RestoreState() + │ └─ Restore health, inventory, position from last checkpoint + │ + ├─ BPC_NarrativeStateSystem → SetChapterFlag("Entry") + └─ GI_GameFramework → SetGamePhase(InGame) +``` + +#### Override: HandlePlayerDead + +``` +HandlePlayerDead (CalledBy: BPC_DeathHandlingSystem dispatcher) + │ Player: Pawn, DeathCause: E_DeathCause, KillerLocation: Vector + │ + ├─ Set bPauseAllowed = false + ├─ Increment GI_HorrorGame.TotalDeaths + │ + ├─ [Check Void Space Conditions] + │ ├─ GI_HorrorGame → GetSessionFlag("VoidSpaceVisited")? → Branch + │ │ ├─ False AND DeathCause == "Void"? + │ │ │ └─ True: + │ │ │ ├─ Set bVoidSpaceActive = true + │ │ │ ├─ BPC_AltDeathSpaceSystem → EnterVoidSpace() + │ │ │ ├─ Transition To Chapter (Chapter.VoidSpace) + │ │ │ └─ Return (void space handles own exit) + │ │ │ + │ │ └─ False → [Normal Death — Respawn] + │ │ + │ └─ [Normal Death Flow] + │ │ + │ ├─ BPC_PersistentCorpseSystem → Leave Corpse (Pawn, DeathCause) + │ ├─ BPC_RunHistoryTracker → Log Death (DeathCause, CurrentChapterTag) + │ │ + │ ├─ BPC_PlayerRespawnSystem → Get Respawn Chapter + │ │ └─ Returns last checkpoint's chapter tag + │ │ + │ ├─ BPC_PersistentWorldStateRecorder → Save World State + │ │ └─ Capture door states, item pickup flags, enemy states + │ │ + │ ├─ SS_SaveManager → Create Auto Save ("DeathRespawn") + │ │ + │ ├─ Transition To Chapter (RespawnChapterTag) + │ │ └─ Parent function loads the level + │ │ + │ └─ (On level loaded) BPC_PlayerRespawnSystem → RestoreState() + │ ├─ Restore inventory (minus items lost on death) + │ ├─ Restore health to checkpoint value + │ └─ Restore world state (doors, items, enemies) + │ + └─ Set bPauseAllowed = true +``` + +#### Override: TriggerEnding + +``` +TriggerEnding (EndingTag: GameplayTag) + │ + ├─ bEndingTriggered? → True → Return (already triggered) + │ + ├─ Set bEndingTriggered = true + ├─ Set bPauseAllowed = false + │ + ├─ GI_HorrorGame → ActiveEndingTag = EndingTag + │ + ├─ BPC_EndingAccumulator → Evaluate Ending (EndingTag) + │ └─ Returns: EndingResult (which final cinematic to play) + │ + ├─ BPC_CutsceneBridge → Play Final Cutscene (EndingResult) + │ └─ Cutscene duration: 30-60 seconds + │ + ├─ (After cutscene completes): + │ ├─ SS_AchievementSystem → Unlock ("CompletedGame") + │ ├─ SS_AchievementSystem → Unlock (EndingTag-specific achievement) + │ └─ BPC_ProgressStatTracker → Log Completion (EndingTag, playtime, deaths) + │ + ├─ Transition To Chapter (Chapter.Credits) + └─ (After credits) OpenLevel → "L_MainMenu" +``` + +#### Custom Function: GetDifficultyScalar + +``` +GetDifficultyScalar() → Float + │ + ├─ Return GameDifficulty × GI_HorrorGame → FearIntensityMultiplier + └─ Clamp result between 0.5 and 2.0 +``` + +#### Custom Function: IsChapterUnlocked + +``` +IsChapterUnlocked (ChapterTag: GameplayTag) → Boolean + │ + ├─ BPC_NarrativeStateSystem → GetChapterFlag (ChapterTag) → Check "IsCompleted" + └─ Return result +``` + +--- + +## Key Differences from `GM_CoreGameMode` Parent + +| Aspect | GM_CoreGameMode (Framework) | GM_HorrorGameMode (Game) | +|--------|---------------------------|-------------------------| +| Default Pawn | Not set (abstract) | `BP_HorrorPlayerCharacter` | +| Player Controller | `PC_CoreController` | `PC_HorrorController` | +| Player State | `PS_CorePlayerState` | `PS_HorrorPlayerState` | +| Game State | `GS_CoreGameState` | `GS_HorrorGameState` | +| HUD | `WBP_HUDController` | `WBP_GameHUDController` | +| Death Handling | Generic respawn | Void space branching, corpse persistence | +| Level Map | Abstract | `HorrorLevelNames` map (tag → level name) | +| Post-login | Generic setup | Opening cutscene + tutorial trigger | + +--- + +## Level Override Configuration + +When creating each `.umap` level, open **World Settings**: + +``` +Window → World Settings + Game Mode Override: GM_HorrorGameMode + (This ensures all levels use the horror game mode) +``` + +Optionally, set chapter-specific settings per level in World Settings: + +| Level | World Settings Override | +|-------|----------------------| +| `L_Asylum_Entry` | `bPauseAllowed: true` (after cutscene) | +| `L_Asylum_VoidSpace` | `bPauseAllowed: false` (pressing ESC shows "No escape from the void") | +| `L_MainMenu` | `bPauseAllowed: false` (main menu has its own pause equivalent via UI) | +| `L_Ending_Truth` | `bPauseAllowed: false` (cutscene lock) | + +--- + +## Blueprint Wiring Checklist + +- [ ] Create BP child of `GM_CoreGameMode` named `GM_HorrorGameMode` in `Game/Core/` +- [ ] Set all 5 Default Classes in Class Defaults +- [ ] Add 4 game-specific variables (bVoidSpaceActive, bEndingTriggered, GameDifficulty, HorrorLevelNames) +- [ ] Populate `HorrorLevelNames` map with all 8 chapter → level mappings +- [ ] Override `Event OnPostLogin` → opener cutscene + tutorial +- [ ] Override `HandlePlayerDead` → void space branch + corpse + respawn +- [ ] Override `TriggerEnding` → cutscene → credits → menu +- [ ] Add `GetDifficultyScalar` function +- [ ] Add `IsChapterUnlocked` function +- [ ] Set `GM_HorrorGameMode` as GameMode Override in each level's World Settings + +--- + +## Communications With + +| Target System | Method | Why | +|--------------|--------|-----| +| `GI_HorrorGame` | GetGameInstance → Cast to GI_HorrorGame | Session data, fear intensity, ending tag | +| `BP_HorrorPlayerCharacter` | Spawned by engine | Player pawn | +| `PC_HorrorController` | Spawned by engine | Input routing | +| `BPC_DeathHandlingSystem` | Dispatcher (receives) | Death events | +| `BPC_AltDeathSpaceSystem` | Direct | Void space entry | +| `BPC_PlayerRespawnSystem` | Direct | Respawn restore | +| `BPC_CutsceneBridge` | Direct | Play/end cutscenes | +| `BPC_EndingAccumulator` | Direct | Evaluate ending | +| `BPC_TutorialSystem` | Direct | Start tutorials | +| `SS_SaveManager` | GetSubsystem | Auto-save on death/respawn | +| `SS_AchievementSystem` | GetSubsystem | Unlock achievements on completion | + +--- + +## Notes for Expansion + +- To add **permadeath mode**: override `HandlePlayerDead` → skip void space → wipe save → restart +- To add **co-op support**: the horror game mode would need to handle multiple player spawns and sync void space +- To add **New Game+**: in `Event OnPostLogin`, check `bIsNewGamePlus` flag → increase `GameDifficulty` → spawn harder enemies +- The `HorrorLevelNames` map approach allows designers to reorder chapters without touching blueprint logic +- Consider adding `OnPreDeath` and `OnPostRespawn` event dispatchers for other systems to bind + +--- + +*GM_HorrorGameMode — Core game mode for Project Void. See [GAMEINDEX.md](GAMEINDEX.md) for full game structure. See [05_GM_CoreGameMode.md](../blueprints/01-core/05_GM_CoreGameMode.md) for parent class spec.* diff --git a/docs/game/enemies-index.md b/docs/game/enemies-index.md new file mode 100644 index 0000000..f355c73 --- /dev/null +++ b/docs/game/enemies-index.md @@ -0,0 +1,371 @@ +# Enemies Index — All Enemy Types + +**Game:** Project Void | **Build Phase:** 12 +**Framework Systems:** 80_BP_EnemyBase, 82_BPC_AlertSystem, 83_BPC_AIStateMachine, 84_AI_BaseAgentController, 85_BB_AgentBoard, 86_BPC_AIMemorySystem, 87_BPC_AIPerceptionSystem, 88_BPC_BehaviourVariantSelector + +--- + +## Purpose + +Defines the 3 enemy types, their AI controllers, behavior trees, blackboards, and integration with the horror framework systems. Plus the friendly NPC survivor. + +--- + +## Enemy Catalog + +| # | Enemy | Threat Level | Speed | Damage | Killable? | Vulnerability | Spawn Areas | +|---|-------|:---:|:---:|:---:|:---:|------|-------------| +| 1 | Patient | Medium | Slow (250) | 20 melee | YES | Light distracts briefly | Entry, WardA, WardB, Basement | +| 2 | Orderly | High | Fast (500) | 15 melee | YES | Flashlight blinds (slows 50%) | WardB, WardensOffice | +| 3 | Shade | Extreme | Teleport | 100 (1-hit) | NO | Light delays approach | WardB², Basement, VoidSpace | + +² Shade appears once in WardB (scripted courtyard) + randomly in morgue + +--- + +## 1. BP_Enemy_Patient — "The Lost" + +**Parent:** `BP_EnemyBase` (80) +**Description:** Former asylum patients, twisted by the void. Slow but persistent. Walk with jerky, unnatural gait. Moan occasionally. Attack by grabbing and slashing. + +### Components + +| Component | Purpose | +|-----------|---------| +| `BP_EnemyBase` components (inherited) | Health (100), movement, skeletal mesh | +| `BPC_AlertSystem` (82) | Suspicious→Alerted→Combat | +| `BPC_AIStateMachine` (83) | Patrol/Search/Combat/Flee | +| `BPC_AIPerceptionSystem` (87) | Sight (120° cone, 2000u), Hearing (1500u) | +| `BPC_AIMemorySystem` (86) | Last known player location | +| `BPC_BehaviourVariantSelector` (88) | Aggressive/Curious/Erratic variants | +| `BPC_DamageReceptionSystem` (72) | Receive damage, hit reaction | +| `BPC_HitReactionSystem` (75) | Flinch/stagger on hit | +| `BPC_DeathCauseTracker` (73) | Log killing blow | + +### AI Controller: AI_PatientController +**Parent:** `AI_BaseAgentController` (84) + +``` +Behavior Tree: BT_PatientBehavior + ├─ Root → Selector + │ ├─ [Combat] Sequence + │ │ ├─ MoveTo (player last known location) + │ │ ├─ Wait (0.5s — "searching" animation) + │ │ ├─ MoveTo (player current location) + │ │ └─ Melee Attack → BPC_MeleeSystem.Swing + │ │ + │ ├─ [Search] Sequence (unseen player, heard sound) + │ │ ├─ MoveTo (suspected location from BPC_AIMemorySystem) + │ │ ├─ Wait (2.0s — "sniffing" animation) + │ │ └─ Return to Patrol + │ │ + │ └─ [Patrol] Sequence (default) + │ ├─ MoveTo (next waypoint on BP_PatrolPath_Asylum) + │ ├─ Wait (3-5s at each waypoint — random) + │ └─ Loop +``` + +### Blackboard: BB_PatientData +**Parent:** `BB_AgentBoard` (85) + +| Key | Type | Purpose | +|-----|------|---------| +| `TargetActor` | Actor | Current target (player) | +| `LastKnownLocation` | Vector | From BPC_AIMemorySystem | +| `AlertState` | Enum | Calm/Suspicious/Alerted/Combat | +| `PatrolIndex` | Int | Current patrol waypoint | +| `bHasSeenPlayer` | Bool | Player was sighted | +| `bHeardSound` | Bool | Sound stimulus received | +| `SoundLocation` | Vector | Where sound came from | + +### Perception + +| Sense | Range | Angle | Notes | +|-------|:---:|:---:|-------| +| **Sight** | 2000u | 120° | Player flashlight increases detection range 50% | +| **Hearing** | 1500u | 360° | Running: detected at full range. Walking: 50%. Crouching: 25% | +| **Damage** | 3000u | 360° | Being shot alerts to player location instantly | +| **Touch** | 100u | 360° | Physical contact = immediate combat | + +### Behaviour Variants (via DA_BehaviourVariant_Patient) + +| Variant | Weight | Behavior | +|---------|:---:|----------| +| **Aggressive** | 40% | Charges directly, ignores hiding spots, attacks on sight | +| **Curious** | 35% | Approaches slowly, tilts head, investigates sounds first | +| **Erratic** | 25% | Randomly changes patrol path, sometimes stands still for 10s | + +### Stress Impact on Player + +- Patient visible: +3 stress/second +- Patient in combat: +5 stress/second +- Patient killed nearby: +10 stress (one-time spike) + +--- + +## 2. BP_Enemy_Orderly — "The Wardens" + +**Parent:** `BP_EnemyBase` (80) +**Description:** Former asylum staff. Fast, aggressive, but vulnerable to bright light. Walk with purposeful stride. Carry rusted medical tools as weapons. + +### Key Differences from Patient + +| Aspect | Patient | Orderly | +|--------|---------|---------| +| Speed | 250 (walk) | 500 (jog) | +| Health | 100 | 75 (weaker but faster) | +| Damage | 20 | 15 (faster attacks) | +| Attack Speed | 2.0s | 1.0s (rapid slashes) | +| Flashlight Effect | Distracts (looks away 2s) | Blinds (slows to 125 speed, misses attacks) | +| Hiding Detection | Can't detect hidden player | Can find hidden player if searched recently | +| Audio Cues | Moans, footsteps | Heavy breathing, keys jangling | + +### Flashlight Vulnerability + +``` +Orderly enters player flashlight beam: + ├─ BPC_AlertSystem.State == Combat? + │ ├─ True: + │ │ ├─ Set movement speed multiplier = 0.25 (slowed) + │ │ ├─ Set attack accuracy multiplier = 0.3 (misses often) + │ │ ├─ Play pained hiss sound + │ │ └─ After 5 seconds in light: Flee state (runs away) + │ │ + │ └─ False: + │ ├─ Set AlertState = Suspicious + │ └─ Shield eyes animation, back away slowly + │ + └─ Flashlight battery drains 2x faster when blinding an Orderly +``` + +### AI Controller: AI_OrderlyController + +Same structure as Patient but with: +- Faster patrol (jog between waypoints) +- Hiding spot investigation behavior (checks lockers) +- Flashlight avoidance task (path around light beams) + +--- + +## 3. BP_Enemy_Shade — "The Void" + +**Parent:** `BP_EnemyBase` (80) +**Description:** The primary antagonist. A shifting, semi-corporeal entity from the void. Cannot be killed, only delayed. Teleports between dark areas. One-hit kills the player. + +### Components + +Same as Patient/Orderly PLUS: +| Component | Purpose | +|-----------|---------| +| `BPC_FearSystem` (90) | Generates fear aura (player stress +8/sec within 800u) | +| `Timeline "PhaseShift"` | Visual: model fades in/out when teleporting | +| `PointLightComponent "VoidGlow"` | Purple-black light aura (grows brighter when close) | + +### Special Rules + +| Rule | Behavior | +|------|----------| +| **Immortal** | Cannot be killed. Damage only delays (stuns for 3-5 seconds) | +| **Teleport** | Moves between dark areas (illuminance < 0.1). Cannot enter lit rooms | +| **Light Delayed** | Flashlight aimed at Shade slows approach speed 60% | +| **Sound Tracking** | FULL hearing range (5000u). Any sound = instant investigation | +| **One-Hit Kill** | Contact causes instant death → Void Space | +| **Void Aura** | Radius 800u: player stress +8/sec, screen vignette darkens | +| **Persistence** | Remembers player across runs (session flag: "ShadeEncountered") | +| **Walls Don't Stop It** | Can phase through doors, walls (not during combat) | + +### AI Controller: AI_ShadeController + +``` +Behavior Tree: BT_ShadeBehavior + ├─ Root → Selector + │ ├─ [Void Hunt] Sequence (player detected by sound/sight) + │ │ ├─ Calculate "dark path" to player (avoid lit areas) + │ │ ├─ Teleport to next dark node (max 3000u per teleport) + │ │ ├─ Wait (1-3s — appears, stands still, watches) + │ │ ├─ MoveTo (player — floating, no walk animation) + │ │ ├─ On contact: KillPlayer → trigger void death + │ │ └─ If flashlight hits: Stun 3s, teleport away + │ │ + │ └─ [Stalk] Sequence (default) + │ ├─ Teleport to dark location near player (800-1500u) + │ ├─ Wait (5-10s — watching) + │ ├─ SFX: whisper, breathing, or distant scream + │ └─ If player stays still > 10s: teleport closer +``` + +### Blackboard: BB_ShadeData + +| Key | Type | Purpose | +|-----|------|---------| +| `TargetPlayer` | Actor | Player reference | +| `DarkNodes` | Array\ | All dark locations in level (pre-calculated) | +| `CurrentDarkNode` | Vector | Current teleport position | +| `bIsStunned` | Bool | Recently hit by flashlight | +| `StunTimer` | Float | Remaining stun duration | +| `bHasKilledPlayer` | Bool | Killed player this encounter | + +### Shade Encounter Design + +| Location | Type | Trigger | Duration | +|----------|------|---------|----------| +| WardB Courtyard | **Scripted Teaser** | Player enters courtyard | 10s (watches then vanishes) | +| WardB Morgue | **Void Shift Guardian** | Void shift event | 30s (pursues during shift) | +| Basement | **Persistent Hunter** | Always active in basement | Entire level | +| Void Space | **Final Chase** | Enter void | Until exit or caught | + +--- + +## 4. BP_NPC_Survivor (Friendly) + +**Parent:** `Character` +**Description:** A surviving asylum patient. Optional rescue NPC. If saved, appears in endings and grants lore. + +### Interaction + +``` +BP_NPC_Survivor + Implements: I_Interactable + +Event Interact: + │ + ├─ [Dialogue Choice] + │ ├─ BPC_DialogueChoiceSystem.PresentChoices() + │ ├─ Options: + │ │ ├─ "Who are you? What happened here?" → Lore dump + │ │ ├─ "Follow me. I'll get you out." → NPC follows player + │ │ └─ "Stay here. It's safer." → NPC stays, gives item + │ │ + │ ├─ Choice → BPC_BranchingConsequenceSystem.ExecuteConsequence() + │ │ ├─ Follow: NPC becomes follower, can carry items + │ │ └─ Stay: NPC gives Keycard Alpha (if not found yet) + │ │ + │ └─ BPC_EndingAccumulator.RecordEvent("SurvivorSaved") + │ + └─ Return True +``` + +### Survivor States + +| State | Behavior | +|-------|----------| +| Idle | Sits in room, waits for player | +| Following | Walks behind player, stops when player stops | +| Hiding | If enemies near, finds hiding spot | +| Dead | If attacked by enemies during escort | +| Saved | Reaches safe room — ending flag set | + +--- + +## Enemy Spawning & Difficulty + +### BPC_ProceduralEncounter (92) Configuration + +| Encounter | DA_EncounterData | Difficulty Scalar | +|-----------|-----------------|:---:| +| WardA Patrol | `DA_Encounter_WardA_Patrol` | ×1.0 | +| WardB Mixed | `DA_Encounter_WardB_Mixed` | ×1.2 | +| Basement Shade | `DA_Encounter_ShadeAmbush` | ×1.5 | + +### BPC_DifficultyManager (89) Scaling + +``` +Player deaths in current chapter: + 0 deaths → Difficulty × 1.0 (normal) + 1 death → Difficulty × 0.9 (slightly easier) + 2 deaths → Difficulty × 0.8 (noticeably easier) + 3+ deaths→ Difficulty × 0.7 (easier + extra checkpoints) + +Difficulty affects: + ├─ Enemy count: × Difficulty (rounded down) + ├─ Enemy health: × Difficulty + ├─ Enemy damage: × Difficulty + ├─ Ammo spawn count: × (2.0 - Difficulty) [more ammo when easier] + ├─ Stress accumulation rate: × Difficulty + └─ Scare event frequency: × Difficulty +``` + +--- + +## Enemy Death & Persistence + +When an enemy is killed: + +``` +Enemy Health = 0 + │ + ├─ [Death Animation] Montage plays (GASP death state) + ├─ [Ragdoll] Enable physics on skeletal mesh + ├─ [Corpse Persistence] + │ ├─ BPC_PersistentCorpseSystem.SaveCorpse() + │ └─ Corpse remains in world until: + │ ├─ Player leaves level (transition) + │ ├─ Save/Load cycle (corpse ID saved) + │ └─ Manual "clear corpses" via Void shift + │ + ├─ [Loot Drop] (30% chance) + │ └─ Spawn random item at corpse location: + │ ├─ Patient: Battery (40%), MedKit (30%), Ammo (20%), Nothing (10%) + │ └─ Orderly: Ammo (40%), Key item hint (30%), Battery (20%), Nothing (10%) + │ + ├─ [Metrics] + │ ├─ BPC_PlayerMetricsTracker.RecordKill(EnemyType, WeaponUsed) + │ └─ BPC_ProgressStatTracker.IncrementEnemiesKilled() + │ + ├─ [Stress Effect] + │ └─ Player BPC_StressSystem.AddStress(10) — "killing a former human" + │ + └─ [Achievement Check] + └─ SS_AchievementSystem.Check("FirstKill", "Pacifist", "Exterminator") +``` + +--- + +## Blueprint Wiring Checklist + +### Patient Enemy +- [ ] Create `BP_Enemy_Patient` — child of `BP_EnemyBase` +- [ ] Add alert, AI state machine, perception, memory, behavior variant, damage reception, hit reaction components +- [ ] Create `AI_PatientController` — child of `AI_BaseAgentController` +- [ ] Create `BT_PatientBehavior` — Patrol/Search/Combat branches +- [ ] Create `BB_PatientData` — all blackboard keys +- [ ] Create `DA_BehaviourVariant_Patient` with 3 variants + weights +- [ ] Set max health = 100, walk speed = 250, damage = 20 + +### Orderly Enemy +- [ ] Create `BP_Enemy_Orderly` — child of `BP_EnemyBase` +- [ ] Same components as Patient +- [ ] Add flashlight vulnerability logic in AI controller (OnPerceptionUpdated → check light) +- [ ] Create `AI_OrderlyController`, `BT_OrderlyBehavior`, `BB_OrderlyData` +- [ ] Set max health = 75, walk speed = 500, damage = 15 + +### Shade Enemy +- [ ] Create `BP_Enemy_Shade` — child of `BP_EnemyBase` +- [ ] Add Teleport, VoidGlow, PhaseShift components +- [ ] Add `BPC_FearSystem` (fear aura) +- [ ] Create `AI_ShadeController` with dark-node pathfinding +- [ ] Create `BT_ShadeBehavior` — VoidHunt/Stalk branches +- [ ] Create `BB_ShadeData` — DarkNodes array, stunned state +- [ ] Set health = invulnerable, damage = 100 (one-hit) + +### NPC Survivor +- [ ] Create `BP_NPC_Survivor` — child of `Character` +- [ ] Implement `I_Interactable` +- [ ] Wire dialogue choices via `BPC_DialogueChoiceSystem` +- [ ] Wire follow/hide behaviors + +--- + +## Notes for Expansion + +- Add **boss encounters**: Void Entity manifestation (multi-phase boss fight) +- Add **stealth kill mechanic**: sneak behind Patient → silent takedown (no stress penalty) +- Add **enemy variants**: "Blind Patient" (hearing-only), "Crawler" (ceiling enemy) +- Add **dynamic music**: combat music intensity scales with number of alert enemies +- Add **enemy faction system**: Patients flee from Shade, Orderlies protect Shade +- Multiplayer: server-authoritative AI, perception events replicated to all clients +- Consider **Procedural placement**: spawn points randomized each new game for replayability + +--- + +*Enemies Index for Project Void. See [GAMEINDEX.md](GAMEINDEX.md) for full game structure. See [80_BP_EnemyBase.md](../blueprints/09-ai/80_BP_EnemyBase.md) for base enemy spec.* diff --git a/docs/game/items-index.md b/docs/game/items-index.md new file mode 100644 index 0000000..cf391f8 --- /dev/null +++ b/docs/game/items-index.md @@ -0,0 +1,360 @@ +# Items Index — All Game Items & Pickups + +**Game:** Project Void | **Build Phases:** 5, 6 +**Framework Systems:** 07_DA_ItemData, 25_BP_ItemPickup, 31_BPC_InventorySystem + +--- + +## Purpose + +Master index of all game items, their Data Assets, and pickup actors. The first 4 items (flashlight, pistol, medkit, keycard) have full build walkthroughs in their own docs. This document covers the remaining 9 items and provides quick-reference for all 13 + their 18 pickups. + +--- + +## Complete Item Catalog + +| # | Item | Type | Stack | Weight | Found In | Has Full Build Doc | +|---|------|------|:---:|:---:|----------|:---:| +| 1 | Flashlight | Tool | 1 | 1.5 | Entry, WardA | [item-flashlight.md](item-flashlight.md) | +| 2 | Pistol (9mm) | Weapon | 1 | 3.0 | WardA (Security Office) | [item-pistol.md](item-pistol.md) | +| 3 | MedKit | Consumable | 5 | 1.0 | All levels | [item-medkit.md](item-medkit.md) | +| 4 | Keycard Omega | KeyItem | 1 | 0.1 | WardA (Nurses Station) | [item-keycard.md](item-keycard.md) | +| 5 | 9mm Ammo | Ammo | 999 | 0.05 | WardA, WardB, Basement | *(below)* | +| 6 | Shotgun | Weapon | 1 | 6.0 | WardB (Morgue) | *(below)* | +| 7 | Shotgun Shells | Ammo | 999 | 0.03 | WardB, Basement | *(below)* | +| 8 | Battery | Consumable | 10 | 0.3 | All dark areas | *(below)* | +| 9 | Adrenaline Syringe | Consumable | 3 | 0.5 | WardB, Basement | *(below)* | +| 10 | Sanity Pill | Consumable | 5 | 0.2 | WardB, Basement, WardensOffice | *(below)* | +| 11 | Crowbar | Tool | 1 | 4.0 | Basement (Boiler Room) | *(below)* | +| 12 | Keycard Alpha | KeyItem | 1 | 0.1 | WardA (Nurses Station) | (same pattern as #4) | +| 13 | Keycard Beta | KeyItem | 1 | 0.1 | WardB (Chapel — behind organ puzzle) | (same pattern as #4) | +| 14 | Warden's Key | KeyItem | 1 | 0.1 | Basement (Morgue Drawer #4) | (same pattern as #4) | +| 15 | Journal Page | Document | 1 | 0.05 | All levels | *(below)* | +| 16 | Patient File | Document | 1 | 0.1 | WardA, WardB, Basement | *(below)* | +| 17 | Old Photo | Collectible | 1 | 0.05 | WardA, WardB, Basement | *(below)* | +| 18 | Void Shard | Collectible | 1 | 0.8 | WardB (1), Basement (1), WardensOffice (1) | *(below)* | + +--- + +## Items with Quick Build Notes + +### 5. DA_Item_Ammo_9mm / BP_Pickup_Ammo_9mm + +**Item Type:** `Ammo` | **Ammo Type Tag:** `Framework.Item.Ammo.Pistol9mm` + +| Field | Value | +|-------|-------| +| `Display Name` | "9mm Rounds" | +| `World Mesh` | `SM_AmmoBox_9mm` (small cardboard box) | +| `AmmoData.PerPickupCount` | `15` (15 rounds per pickup) | + +**Pickup Wiring (differs from standard pickups):** +``` +Event Interact → Interactor → BPC_AmmoComponent.AddAmmo(AmmoTypeTag, PerPickupCount) + └─ (Does NOT use BPC_InventorySystem — ammo goes to ammo pool, not inventory grid) +``` + +--- + +### 6. DA_Item_Shotgun / BP_Pickup_Shotgun / BP_Shotgun_Held + +**Item Type:** `Weapon` | **Slot:** `Framework.Equipment.Slot.PrimaryWeapon` + +| Field | Value | +|-------|-------| +| `Display Name` | "Double-Barrel Shotgun" | +| `Weight` | `6.0` | +| `Equipment.Damage` | `45.0` (total if both barrels hit) | +| `Equipment.FireRate` | `1.0` (slow — break-action reload) | +| `Equipment.MagazineSize` | `2` | +| `Equipment.ReloadTime` | `3.0` (shell-by-shell) | + +**Difference from Pistol:** +- **Pellet Spread:** Fire trace replaced with multi-trace (6 pellets in cone) +- **Reload:** Shell-by-shell reload (player can fire mid-reload if 1 shell loaded) +- **Damage Falloff:** Heavy dropoff past 2000 units (close-range weapon) +- **Recoil:** Much heavier — BPC_RecoilSystem.ApplyRecoil with ×3 multiplier + +**Held Actor Components:** +``` +BP_Shotgun_Held + ├── SkeletalMeshComponent "WeaponMesh" (break-action animation) + ├── SceneComponent "MuzzleSocket" (barrel end) + ├── AudioComponent "FireSound" (loud BOOM) + ├── AudioComponent "ReloadSound" (shell click) + └── ParticleSystemComponent "MuzzleFlash" (large flash) +``` + +--- + +### 7. DA_Item_Ammo_Shells / BP_Pickup_Ammo_Shells + +**Item Type:** `Ammo` | **Ammo Type Tag:** `Framework.Item.Ammo.ShotgunShells` + +| Field | Value | +|-------|-------| +| `Display Name` | "12-Gauge Shells" | +| `AmmoData.PerPickupCount` | `6` | + +Same pickup pattern as 9mm ammo — routes to `BPC_AmmoComponent.AddAmmo()`. + +--- + +### 8. DA_Item_Battery / BP_Pickup_Battery + +**Item Type:** `Consumable` | **Stack Limit:** `10` + +| Field | Value | +|-------|-------| +| `Display Name` | "Flashlight Battery" | +| `Consumable.RestoreValue` | `50.0` (flashlight charge percentage) | +| `Consumable.UseDuration` | `1.0` | + +**Usage (in BPC_ConsumableSystem):** +``` +Use Battery → Get equipped flashlight → I_Toggleable.GetCurrentState() + ├─ If ON: FlashlightCharge += 50.0 (clamped to 100) + └─ If OFF: FlashlightCharge += 50.0 + auto-toggle ON +``` + +--- + +### 9. DA_Item_AdrenalineSyringe / BP_Pickup_AdrenalineSyringe + +**Item Type:** `Consumable` | **Stack Limit:** `3` + +| Field | Value | +|-------|-------| +| `Display Name` | "Adrenaline Syringe" | +| `Description` | "A military-grade stimulant. Restores stamina and suppresses exhaustion." | +| `Consumable.StaminaRestore` | `50.0` | +| `Consumable.ExhaustionBlockDuration` | `10.0` (seconds — prevents exhaustion state) | +| `Consumable.UseDuration` | `1.5` (injection animation) | +| `Consumable.StressIncrease` | `10.0` (side effect — jittery) | + +**Usage:** +``` +BPC_ConsumableSystem.UseItem(DA_Item_AdrenalineSyringe) + ├─ BPC_StaminaSystem.RestoreStamina(50.0) + ├─ BPC_StaminaSystem.SetExhaustionBlocked(true, 10.0s) + ├─ BPC_StressSystem.AddStress(10.0) // side effect + ├─ WBP_ScreenEffectController.Flash(amber, 0.5s) + └─ BPC_CameraStateLayer.AddTemporaryLayer("Adrenaline", 10s) + └─ FOV +5, slight chromatic aberration +``` + +--- + +### 10. DA_Item_SanityPill / BP_Pickup_SanityPill + +**Item Type:** `Consumable` | **Stack Limit:** `5` + +| Field | Value | +|-------|-------| +| `Display Name` | "Sanity Pill (Diazepam)" | +| `Description` | "Prescribed to asylum patients. Reduces psychological stress." | +| `Consumable.StressReduce` | `30.0` | +| `Consumable.UseDuration` | `2.0` | +| `Consumable.StaminaReduce` | `10.0` (side effect — drowsiness) | + +**Usage:** +``` +BPC_ConsumableSystem.UseItem(DA_Item_SanityPill) + ├─ BPC_StressSystem.RemoveStress(30.0) + ├─ BPC_StaminaSystem.DrainStamina(10.0) // side effect + ├─ WBP_ScreenEffectController.Flash(blue-white, 1.0s) + ├─ BPC_MemoryDriftSystem.SetIntensity(0.0) // clears hallucinations + └─ SS_AudioManager.SetFloatParameter("StressTier", newTier) +``` + +--- + +### 11. DA_Item_Crowbar / BP_Pickup_Crowbar / BP_Crowbar_Held + +**Item Type:** `Tool` | **Slot:** `Framework.Equipment.Slot.Tool` (shares with flashlight) + +| Field | Value | +|-------|-------| +| `Display Name` | "Rusted Crowbar" | +| `Weight` | `4.0` | +| `Equipment.Damage` | `20.0` (melee damage) | + +**Dual functions:** +1. **Pry open barricaded doors** — `BP_Door_Barricaded` checks for crowbar in equipment +2. **Melee weapon** — `BPC_MeleeSystem` handles swing hit detection + +``` +BP_Crowbar_Held implements: + ├─ I_UsableItem → UseItem() → BPC_MeleeSystem.Swing() + ├─ I_MeleeWeapon → GetDamage() → 20.0 + └─ I_Tool → CanPryOpen(DoorActor) → true +``` + +--- + +### 12-14. Key Items (Alpha, Beta, Warden's Key) + +All follow the **exact same pattern** as [item-keycard.md](item-keycard.md): +- `DA_ItemData` with `ItemType = KeyItem`, `bIsKeyItem = true` +- `BP_Pickup_*` with standard Construction Script + `I_Interactable` +- `BP_Door_*` with `RequiredKeyTag` matching the key +- `BPC_KeyItemSystem` handles consumption on use + +| Key | Unlocks | Location | +|-----|---------|----------| +| Keycard Alpha | WardA → WardB transition door | Nurses Station (WardA) | +| Keycard Beta | WardB → Basement transition door | Chapel (behind organ puzzle) | +| Warden's Key | Basement → WardensOffice (elevator) | Morgue Drawer #4 (puzzle reward) | + +--- + +### 15. DA_Item_JournalPage / BP_Pickup_JournalPage + +**Item Type:** `Document` + +| Field | Value | +|-------|-------| +| `Display Name` | "Journal Page" (varies per instance — set via Instance Data) | +| `DocumentData.PageCount` | `1` | +| `DocumentData.bIsLoreEntry` | `true` | + +**Variants (set via Instance Editable on pickup):** + +| Instance | Display Name | Content | +|----------|-------------|---------| +| `JournalPage_WakeUp` | "A Note to Myself" | Player's own handwriting — "Find out what happened here" | +| `JournalPage_WardensConfession` | "Warden's Confession" | Final journal of Dr. Blackwood — reveals entity origin | +| `JournalPage_PatientLetter` | "Letter to Mother" | Patient's unsent letter — personal horror story | +| `JournalPage_OrderlyReport` | "Incident Report #77" | Orderly's report of first void manifestation | + +--- + +### 16. DA_Item_PatientFile / BP_Pickup_PatientFile + +**Item Type:** `Document` | **DocumentData.PageCount:** `2-3` + +Patient files are world-building collectibles. Each file has: patient name, admission date, diagnosis, doctor's notes, treatment log. Reading all files unlocks lore entries via `BPC_LoreUnlockSystem`. + +**6 Files scattered across WardA, WardB, Basement:** +1. Patient #47 — "Claims to hear voices from walls" +2. Patient #12 — "Exhibits void-touched scarring" +3. Patient #88 — "Disappeared from locked cell — no explanation" +4. Patient #03 — "First patient — died during initial void breach (1923)" +5. Patient #55 — "Catatonic — responds only to old asylum music" +6. Patient #101 — "No records prior to admission. Identity unknown." + +--- + +### 17. DA_Item_OldPhoto / BP_Pickup_OldPhoto + +**Item Type:** `Collectible` | **bIsCollectible:** `true` + +4 photos hidden across levels. Each shows a piece of the asylum's history. Tracked by `BPC_CollectibleTracker`. Finding all 4 unlocks "Photographer" achievement. + +| Photo | Location | Content | +|-------|----------|---------| +| Photo 1 | WardA — Treatment Room bed | Asylum staff photo, 1920 — faces scratched out | +| Photo 2 | WardB — Morgue desk | Patient group photo — one figure is missing from print | +| Photo 3 | Basement — Crematorium | Dr. Blackwood standing before the void portal | +| Photo 4 | WardensOffice — Bookshelf | Player's own reflection — dated 1923 | + +--- + +### 18. DA_Item_VoidShard / BP_Pickup_VoidShard + +**Item Type:** `Collectible` | **bIsCollectible:** `true` + +3 shards hidden throughout the game. Critical for ending determination. Each shard glows with void energy. Tracked by `BPC_CollectibleTracker`. + +| Shard | Location | Acquired | +|-------|----------|----------| +| Shard 1 | WardB — Courtyard statue (behind) | Mid-game | +| Shard 2 | Basement — Crematorium (on altar) | Late-game | +| Shard 3 | WardensOffice — Bookshelf (hidden slot) | End-game | + +**Ending Impact:** +- 0 shards → Ending A (Resist / Escape) — default +- 1-2 shards → Ending B (Merge / Become) — possible +- 3 shards → Ending C (Sacrifice / Seal) — unlocked + alternate dialogue + +--- + +## Pickup Actor Template (for all 18 pickups) + +Every pickup in the game follows this exact Component + Wiring template: + +``` +BP_Pickup_[ItemName] + Components: + ├── StaticMeshComponent "Mesh" (collision disabled) + └── SphereComponent "PickupTrigger" (radius 200, OverlapOnlyPawn) + + Variables: + └── ItemData (DA_ItemData*, Instance Editable) + + Construction Script: + ├── ItemData.IsValid? + │ ├─ True: + │ │ ├─ ItemData → GetWorldMesh → LoadSynchronous → SetStaticMesh(Mesh) + │ │ └─ ItemData → GetDisplayName → SetActorLabel + │ └─ False: Print "No ItemData configured" + + Interfaces: + └── I_Interactable + + Event Interact: + ├─ [GET INVENTORY] Interactor → BPC_InventorySystem + ├─ [CHECK CAPACITY] CanAddItem(ItemData, 1) + │ ├─ True: + │ │ ├─ AddItem(ItemData, 1) + │ │ ├─ [IF WEAPON/TOOL] Auto-equip to slot + │ │ ├─ DestroyActor() + │ │ └─ Return True + │ └─ False: + │ ├─ Print "Inventory full" + │ └─ Return False + + Event CanInteract: + └─ Return (ItemData.IsValid?) + + Event GetInteractionPrompt: + └─ Return Format("Pick up {0}", ItemData.DisplayName) + + Event BeginPlay (optional): + └─ Start float/bob Timeline + + SPECIAL HANDLERS (per item type): + ├─ Ammo: Interactor → BPC_AmmoComponent.AddAmmo() (NOT inventory) + ├─ Weapon: After AddItem → BPC_EquipmentSlotSystem.EquipToSlot() + └─ Key Item: After AddItem → BPC_KeyItemSystem.RegisterKey() +``` + +--- + +## Item Type Comparison + +| Aspect | Weapon | Tool | Consumable | Ammo | KeyItem | Document | Collectible | +|--------|--------|------|-----------|------|---------|----------|-------------| +| Inventory Slot | Yes | Yes | Yes | No (pool) | Yes | Yes | Yes | +| Stack Limit | 1 | 1 | 3-10 | 999 | 1 | 1 | 1 | +| Can Drop? | Yes | Yes | Yes | N/A | NO | Yes | Yes | +| Equips? | Yes (Primary/Tool) | Yes (Tool) | No | No | No | No | No | +| Consumed? | No (ammo) | No (battery) | Yes | Yes | Yes | No | No | +| Saves? | Yes | Yes | Yes | Yes | Yes | Yes | Yes | +| Interfaces | I_UsableItem | I_UsableItem, I_Toggleable | I_UsableItem | — | — | — | — | + +--- + +## Notes for Expansion + +- Add **upgradable flashlight**: find "High-Power Bulb" → increases light radius/cone +- Add **silencer attachment** for pistol (consumable, degrades over 10 shots) +- Add **crafting recipes**: combine items via `BPC_ItemCombineSystem` (e.g., Rag + Alcohol = Molotov) +- Add **randomized item placement** per run via `BPC_ProceduralEncounter` loot tables +- Add **inspect mode**: hold item in hand, rotate to find hidden details (some documents have invisible ink) +- Void Shards could have **puzzle interactions** — place all 3 in a pedestal to unlock secret area +- Patient Files could have **audio logs** attached — play via `BPC_DialoguePlaybackSystem` + +--- + +*Items Index for Project Void. See [GAMEINDEX.md](GAMEINDEX.md) for full game structure. See individual item docs for detailed build walkthroughs.* diff --git a/docs/game/levels.md b/docs/game/levels.md new file mode 100644 index 0000000..0c6d46a --- /dev/null +++ b/docs/game/levels.md @@ -0,0 +1,642 @@ +# Level Design — All 11 Maps + +**Game:** Project Void | **Build Phases:** 2 (splash/menu/test) + 21 (full levels) +**Framework Systems:** 05_GM_CoreGameMode, 19_BP_DoorActor, 37_BP_Checkpoint, 67_BP_NarrativeTriggerVolume, 92_BPC_ProceduralEncounter, 93_BPC_AdaptiveEnvironmentDirector, 133_BP_RoomAudioZone + +--- + +## Purpose + +Defines the layout, objectives, placed assets, and atmosphere for every `.umap` level in the game. Each level builds on the previous, introducing new systems incrementally. + +--- + +## Level Index + +| # | Map File | Chapter Tag | Type | Playtime (est.) | +|---|----------|-------------|------|:---:| +| 1 | `L_SplashScreen` | — | Boot | 0:08 | +| 2 | `L_MainMenu` | — | Menu | ∞ | +| 3 | `L_Asylum_Entry` | `Chapter.Entry` | Tutorial | 5 min | +| 4 | `L_Asylum_WardA` | `Chapter.WardA` | Exploration | 20 min | +| 5 | `L_Asylum_WardB` | `Chapter.WardB` | Horror Ramp | 25 min | +| 6 | `L_Asylum_Basement` | `Chapter.Basement` | Dark Horror | 20 min | +| 7 | `L_Asylum_WardensOffice` | `Chapter.WardensOffice` | Climax | 15 min | +| 8 | `L_Asylum_VoidSpace` | `Chapter.VoidSpace` | Alt Reality | 10 min | +| 9 | `L_Ending_Truth` | `Chapter.Ending` | Cinematic | 5 min | +| 10 | `L_Credits` | `Chapter.Credits` | Credits | 1 min | +| 11 | `L_Test_Sandbox` | — | Dev | ∞ | + +--- + +## L1 — L_SplashScreen + +**Duration:** ~8 seconds (skippable) | **Framework:** 114_WBP_SplashScreen + +``` +Setup: + 1. New Level → Empty Level + 2. Add PostProcessVolume: + - Set to Unbound: true + - Film → Saturation: 0.9 + - Color Grading → cool desaturated look + 3. Add WBP_SplashHorror widget to viewport on BeginPlay + 4. Level Blueprint: OnBeginPlay → PlaySplashSequence + 5. OnSequenceComplete: OpenLevel("L_MainMenu") + +Placed Assets: + - (None — this is a UI-only level with no world geometry) + - Optional: add a CameraActor + black VoidPlane for background + +Audio: + - SS_AudioManager.PlayMusic("splash_ambient", MS_MusicBus) +``` + +--- + +## L2 — L_MainMenu + +**Framework:** 44_SS_UIManager, 51_WBP_MainMenu + +``` +Layout: + ┌─────────────────────────────────────────────┐ + │ │ + │ 3D Environment: Asylum Hallway │ + │ ├─ Static Mesh: Hallway section │ + │ ├─ Dim point lights (3) │ + │ ├─ ExponentialHeightFog (density 0.02) │ + │ ├─ PostProcessVolume (horror color grade) │ + │ └─ CameraActor: Slow left-right pan │ + │ (animate rotation via Timeline │ + │ in Level Blueprint) │ + │ │ + │ WBP_GameMainMenu overlaid on screen │ + │ │ + └─────────────────────────────────────────────┘ + +Placed Assets: + ├─ StaticMesh: SM_AsylumHallway (whitebox) + ├─ PointLight × 3 (flicker enabled via BP_LightEvent_Flicker) + ├─ PostProcessVolume (horror color grade) + ├─ ExponentialHeightFog + ├─ CameraActor ("MenuCam") + └─ PlayerStart (hidden — used for spawn, camera overrides) + +Level Blueprint: + OnBeginPlay + ├─ SS_UIManager.ShowMenu(MainMenu) + ├─ CameraActor → SetViewTargetWithBlend + └─ Play menu ambient music via SS_AudioManager + +Audio: + ├─ BP_RoomAudioZone → Lobby preset + └─ SS_AudioManager.PlayMusic("menu_theme", MS_MusicBus) +``` + +--- + +## L3 — L_Asylum_Entry (Tutorial) + +**Duration:** ~5 minutes | **Player Start:** Padded Cell + +``` +Layout (linear): + ┌─────────┐ ┌─────────┐ ┌──────────────┐ ┌─────────┐ + │ PADDED │───→│ HALLWAY │───→│ NURSE STATION│───→│ EXIT │ + │ CELL │ │ A (dark)│ │ (safe room) │ │ DOOR │ + │ │ │ + vent │ │ │ │ │ + │ [Wake] │ └────┬────┘ └──────────────┘ └────┬────┘ + └─────────┘ │ │ + │ (vent path) ▼ + ┌────┴────┐ L_Asylum_WardA + │ STORAGE │ + │ CLOSET │ + └─────────┘ + +Systems Demonstrated: + ├─ BPC_InteractionDetector (pick up note, open door) + ├─ BPC_MovementStateSystem (walk, crouch through vent) + ├─ BPC_HidingSystem (hide in storage locker) + ├─ BPC_InventorySystem (first item: "Note") + ├─ BPC_ObjectiveSystem (first objective: "Find a way out") + ├─ BPC_TutorialSystem (4 tutorial prompts) + ├─ BPC_StateManager (hiding blocks interaction, walking allowed) + └─ BP_Checkpoint (first safe room checkpoint) + +Placed Assets: + ├─ [Cell] + │ ├─ PlayerStart ("WakeupSpawn") + │ ├─ BP_Door_Asylum (unlocked — exit cell) + │ ├─ BP_Pickup_JournalPage ("Wake Up Note" — ItemData: DA_Item_JournalPage) + │ └─ BP_NarrativeTrigger_Intro (starts opening cutscene) + │ + ├─ [Hallway A] + │ ├─ BP_Door_Asylum × 2 (unlocked) + │ ├─ Vent entrance (overlap → prompt crouch) + │ ├─ BP_Pickup_Battery × 2 (on floor) + │ └─ [SCRIPTED] First Patient sighting + │ └─ BP_Enemy_Patient walks past hallway window + │ (behind glass, cannot reach player) + │ Trigger: OnOverlap with hallway midpoint trigger + │ + ├─ [Storage Closet] + │ ├─ BP_HidingSpot_Locker × 3 + │ ├─ BP_Pickup_MedKit (on shelf) + │ └─ BP_Door_Asylum (unlocked → back to hallway) + │ + ├─ [Nurse Station] + │ ├─ BP_Checkpoint_SafeRoom (first checkpoint) + │ ├─ BP_Desk_Drawer × 2 (contain: battery, journal page) + │ ├─ BP_Pickup_Flashlight (on desk — critical item) + │ └─ WBP_NotificationToast: "Objective Updated: Find the Warden's Key" + │ + └─ [Exit Door] + └─ BP_Door_OmegaWing (UNLOCKED for tutorial — opens to WardA) + In full game: requires keycard; tutorial version is pre-unlocked + +Tutorial Triggers: + ├─ BP_TutorialTrigger_Controls → "Use WASD to move, Mouse to look" + ├─ BP_TutorialTrigger_Interact → "Press E to interact with objects" + ├─ BP_TutorialTrigger_Crouch → "Press C to crouch through vents" + └─ BP_TutorialTrigger_Hiding → "Press E on lockers to hide from threats" + +Audio: + ├─ BP_RoomAudioZone → SmallRoom preset (cell + hallway) + ├─ At intro: SS_AudioManager.PlayDialogue("intro_voiceover") (whispered) + └─ First patient: scare sting + heartbeat rise + +Atmosphere: + ├─ DA_Atmosphere_WardA_Standard (light tension) + ├─ BP_LightEvent_Flicker (in hallway — occasional flicker) + └─ BPC_AtmosphereStateController: Tension = Low +``` + +--- + +## L4 — L_Asylum_WardA (Main Exploration) + +**Duration:** ~20 minutes | **Framework systems:** All interaction, inventory, weapons + +``` +Layout (hub-and-spoke): + ┌──────────────┐ + │ MAIN HALL │ ← Patient enemies patrol + └──┬───┬───┬──┘ + │ │ │ + ┌────────────┘ │ └────────────┐ + ▼ ▼ ▼ + ┌────────────┐ ┌────────────┐ ┌────────────┐ + │ TREATMENT │ │ SECURITY │ │ NURSES' │ + │ ROOM │ │ OFFICE │ │ STATION │ + │ (locked) │ │ (pistol) │ │ (keycard) │ + └────────────┘ └────────────┘ └────────────┘ + +Systems Demonstrated: + ├─ BP_DoorActor (locked/unlocked/barricaded states) + ├─ BP_PuzzleDeviceActor (fusebox — restore power) + ├─ BPC_ContainerInventory (desk drawers, filing cabinets) + ├─ BP_WeaponBase → BP_Pistol_Held (first weapon) + ├─ BPC_FirearmSystem (hitscan fire) + ├─ BPC_AmmoComponent (9mm ammo pickup) + ├─ BPC_RecoilSystem (pistol recoil) + ├─ BPC_ReloadSystem (pistol reload) + ├─ BPC_CombatFeedbackComponent (hit markers) + ├─ BP_EnemyBase → BP_Enemy_Patient (first real enemy) + ├─ BPC_AlertSystem (suspicious → alerted → combat) + ├─ BPC_AIPerceptionSystem (sight + hearing detection) + ├─ BPC_DamageReceptionSystem (enemy takes damage) + └─ BPC_HitReactionSystem (enemy flinches when shot) + +Placed Assets: + ├─ [Main Hall] + │ ├─ PlayerStart (from Entry transition) + │ ├─ BP_PatrolPath_Asylum × 2 (for Patient enemies) + │ ├─ BP_Enemy_Patient × 2 (patrolling) + │ ├─ BP_Pickup_Ammo_9mm × 3 (scattered on floor/tables) + │ ├─ BP_Pickup_MedKit × 2 + │ ├─ BP_Pickup_Battery × 2 + │ └─ BP_HidingSpot_Locker × 3 + │ + ├─ [Treatment Room] (locked initially) + │ ├─ BP_Door_Asylum (locked — unlocks after fusebox puzzle) + │ ├─ BP_Pickup_OldPhoto × 1 (on bed) + │ └─ BP_Pickup_PatientFile × 1 (on desk) + │ + ├─ [Security Office] + │ ├─ BP_Pickup_Pistol (on desk — first weapon) + │ ├─ BP_Pickup_Ammo_9mm × 2 + │ ├─ BP_Desk_Drawer × 2 + │ └─ BP_DiegeticScreen_Monitor (shows hallway camera feed) + │ + ├─ [Nurses' Station] + │ ├─ BP_Pickup_KeycardAlpha (on counter — key item) + │ ├─ BP_Pickup_MedKit × 2 + │ ├─ BP_FilingCabinet × 2 (contain: patient files, journal pages) + │ └─ BP_Checkpoint_Mirror (checkpoint) + │ + └─ [Puzzles] + └─ BP_Puzzle_Fusebox (in utility closet off main hall) + ├─ Solution: Flip 3 switches in correct order (diagram nearby) + ├─ Reward: Treatment room unlocks + └─ BPC_TrialScenarioSystem: no timer, just puzzle + +Encounter Data: + ├─ DA_Encounter_WardA_Patrol + │ ├─ Enemy: Patient + │ ├─ Count: 2 (spawned at start) + │ └─ Respawn: OFF (once killed, stays dead — check persistence) + │ + └─ BP_EncounterSpawner_WardA (procedural) + ├─ Spawns additional Patient if player lingers > 5 min in one spot + └─ Difficulty: Easy + +Audio: + ├─ BP_RoomAudioZone_WardA × 4 (one per room) + │ └─ DA_RoomAcoustic_WardA (medium reverb, echo) + ├─ SS_AudioManager.SetFloatParameter("TensionLevel", 0.3) + └─ Scare sting on first patient sighting + +Atmosphere: + ├─ DA_Atmosphere_WardA_Standard + │ ├─ Lighting: Dim overheads with occasional flicker + │ ├─ Fog: Light volumetric + │ ├─ PostProcess: Desaturated, cool blue tint + │ └─ Music: Low drone, no beat + │ + ├─ Scares: + │ ├─ DA_Scare_LockerJumpscare → locker rattles when player nearby + │ └─ DA_Scare_WindowFace → shadow passes window + +Adaptive Environment: + ├─ BPC_DifficultyManager: starts at 1.0 + │ └─ If player dies: decrease to 0.8 (fewer enemies, more ammo) + └─ BPC_PacingDirector: Exploration→Tension→Encounter→Recovery bands +``` + +--- + +## L5 — L_Asylum_WardB (Horror Ramp) + +**Duration:** ~25 minutes + +``` +Layout (corridor maze with central courtyard): + ┌──────────────────────────────────┐ + │ CENTRAL COURTYARD │ + │ (overgrown, rain effect, │ + │ Shade first appearance) │ + └────────┬──────────┬──────────────┘ + │ │ + ┌─────────────┘ └─────────────┐ + ▼ ▼ +┌──────────┐ ┌──────────────┐ +│ MORGUE │ │ CHAPEL │ +│ (shotgun) │ │ (pipe organ │ +│ + void │ │ puzzle) │ +│ trigger │ │ │ +└──────────┘ └──────────────┘ + +Key systems introduced: + ├─ BP_Enemy_Orderly (fast melee, flashlight-weak) + ├─ BP_Enemy_Shade (teleporting, rare, terrifying) + ├─ BPC_AltDeathSpaceSystem → Void shift trigger + ├─ BP_Puzzle_PipeOrgan (music puzzle) + ├─ BPC_MemoryDriftSystem (first hallucinations) + ├─ BPC_FearSystem (enemy fears flashlight on Orderly) + └─ DA_ScareEvent × 3 (mirror face, window slam, breathing) + +Placed Key Assets: + ├─ BP_Shotgun_Held (in morgue, drawer 7) + ├─ BP_Pickup_Ammo_Shells × 5 + ├─ BP_Pickup_AdrenalineSyringe × 2 + ├─ BP_Pickup_SanityPill × 2 + ├─ BP_Pickup_KeycardBeta (behind organ puzzle) + ├─ BP_Pickup_VoidShard × 1 (hidden in courtyard statue) + ├─ BP_Pickup_PatientFile × 3 + └─ BP_Pickup_OldPhoto × 1 + +Enemies: + ├─ BP_Enemy_Orderly × 2 (fast, patrol chapel and courtyard) + ├─ BP_Enemy_Patient × 1 (morgue hallway) + └─ BP_Enemy_Shade × 1 (scripted courtyard appearance + random morgue) + +Void Shift Trigger (in Morgue): + On overlap with morgue center trigger: + ├─ BPC_AdaptiveEnvironmentDirector → TriggerRoomMutation("VoidShift") + ├─ Effects: + │ ├─ BPC_LightEventController → All lights flicker red + │ ├─ BP_Door_Void → Appears/disappears + │ ├─ BPC_MemoryDriftSystem → SetIntensity(0.5) + │ │ └─ Walls appear to breathe, text on wall shifts + │ └─ SS_AudioManager → Crossfade to void ambience + ├─ Duration: 30 seconds + └─ Exit: Find mirror in morgue, interact → shift ends + +Audio: + ├─ BP_RoomAudioZone_WardB (darker reverb, echo chamber feel) + ├─ BP_RoomAudioZone_Courtyard (outdoor preset + rain) + └─ Organ puzzle: each key press plays a pipe note +``` + +--- + +## L6 — L_Asylum_Basement (Dark Horror) + +**Duration:** ~20 minutes | **Special Rule:** No ambient light + +``` +Layout (dark maze): + ┌─────────────────────────────────────────┐ + │ BASEMENT CORRIDORS │ + │ (zero ambient light — flashlight only) │ + │ │ + │ ┌─────┐ ┌──────┐ ┌──────┐ │ + │ │BOILER│ │MORGUE│ │CREMAT│ │ + │ │ ROOM │ │ #4 │ │ORIUM │ │ + │ └──┬───┘ └──┬───┘ └──┬───┘ │ + │ │ │ │ │ + │ └──────────┼──────────┘ │ + │ │ │ + │ ┌──────┴──────┐ │ + │ │ ELEVATOR │ → WardensOffice │ + │ │ (requires │ │ + │ │ WardKey) │ │ + │ └─────────────┘ │ + └─────────────────────────────────────────┘ + +Key systems demonstrated: + ├─ BPC_StressSystem (max stress from constant darkness) + ├─ BPC_EndingAccumulator (morgue puzzle reveals ending-lore) + ├─ BPC_ScareEventSystem (4+ jump scares) + ├─ BPC_AdaptiveEnvironmentDirector (maze shifts rooms) + ├─ BPC_AIMemorySystem (Shade remembers last known location) + └─ BPC_PersistentCorpseSystem (enemy corpses remain) + +Placed Assets: + ├─ BP_Pickup_Key_Wardens (in morgue drawer #4 — behind puzzle) + ├─ BP_Pickup_Battery × 4 (critical — scattered in dark) + ├─ BP_Pickup_Ammo_9mm × 3 + ├─ BP_Pickup_Ammo_Shells × 3 + ├─ BP_Pickup_MedKit × 2 + ├─ BP_Pickup_AdrenalineSyringe × 1 + ├─ BP_Pickup_SanityPill × 1 + ├─ BP_Pickup_VoidShard × 1 (hidden in crematorium) + └─ BP_Pickup_OldPhoto × 1 + +Puzzle: BP_Puzzle_MorgueDrawers + ├─ 12 morgue drawers in cold storage room + ├─ Solution: Open drawers in order matching patient death dates + │ └─ Clue found in patient files from WardA + WardB + ├─ Wrong drawer: Shade appears behind player briefly + └─ Correct sequence: Drawer #4 opens → contains Warden's Key + +Enemies: + └─ BP_Enemy_Shade × 1 (persistent pursuer) + ├─ Teleports between dark corners + ├─ Flashlight delays its approach (slows to 50% speed) + ├─ Cannot be killed — only avoided + └─ If caught → instant death → Void Space + +Stress Mechanics: + ├─ Player stress rises constantly (+2/sec) in total darkness + ├─ Using flashlight: stress rise slows to +0.5/sec + ├─ Battery depletes: stress rise doubles + ├─ At Breaking (75+): screen distortion, false enemy sounds + └─ At Catatonic (100): player collapses, Shade appears + +Audio: + ├─ BP_RoomAudioZone_Basement (heavy reverb, dripping water, distant screams) + ├─ SS_AudioManager.SetFloatParameter("HeartRate", player heart rate) + └─ 4 scare sting cues placed at specific trigger volumes + +Atmosphere: + ├─ NO PostProcess volume "auto-exposure" — pure darkness without flashlight + ├─ BP_LightEvent_Flicker × 6 (throughout basement — random flickers) + └─ DA_Atmosphere_Basement_Dark → maximum horror preset +``` + +--- + +## L7 — L_Asylum_WardensOffice (Climax) + +**Duration:** ~15 minutes + +``` +Layout (single large room + connecting office): + ┌───────────────────────────────────────┐ + │ WARDEN'S OFFICE │ + │ ├─ Large desk (Warden's Journal) │ + │ ├─ Bookshelves (cover positions) │ + │ ├─ Window overlooking Void (reality │ + │ │ breaking through glass) │ + │ │ │ + │ ├─ [DIALOGUE CHOICE TRIGGER] │ + │ │ └─ Entity speaks through mirror │ + │ │ 3 choices: │ + │ │ A) Resist → fight │ + │ │ B) Accept → merge │ + │ │ C) Sacrifice → seal void │ + │ │ │ + │ └─ Ending Accumulator evaluates │ + │ └─ TriggerEnding(result) │ + └───────────────────────────────────────┘ + +Key systems: + ├─ BPC_DialogueChoiceSystem (3-way choice) + ├─ BPC_BranchingConsequenceSystem (choice → ending path) + ├─ BPC_EndingAccumulator (evaluate all conditions) + └─ BPC_CutsceneBridge (play ending cinematic) + +Placed Assets: + ├─ BP_Pickup_JournalPage ("Warden's Confession") on desk + ├─ BP_Pickup_VoidShard × 1 (final — on bookshelf) + ├─ BP_DiegeticScreen_Monitor (shows player's own reflection → void) + └─ BP_NarrativeTrigger_Ending (triggers final confrontation) +``` + +--- + +## L8 — L_Asylum_VoidSpace (Alt Reality) + +**Duration:** ~10 minutes | **Access:** Death by Shade OR void portal in Basement + +``` +Layout (non-Euclidean, floating geometry): + + A series of disconnected rooms floating in black void. + Corridors shift — walking forward may loop back. + + ┌──────┐ ┌──────┐ ┌──────┐ + │MEMORY│ ──→ │MEMORY│ ──→ │MEMORY│ + │SHRINE│ │SHRINE│ │SHRINE│ + │ 1 │ │ 2 │ │ 3 │ + └──────┘ └──────┘ └──────┘ + │ │ │ + └───────────┼───────────┘ + │ + ┌────┴────┐ + │ EXIT │ → Return to asylum + │ PORTAL │ + └─────────┘ + +Systems: + ├─ BPC_AltDeathSpaceSystem (enter/exit void) + ├─ BPC_TrialScenarioSystem (timed escape — 5 minutes) + └─ BPC_AdaptiveEnvironmentDirector (dynamic room shifting) + +Placed Assets: + ├─ BP_PuzzleDeviceActor × 3 (memory shrines — interact to "remember") + ├─ BP_Pickup_VoidShard × 1 (if not already collected) + ├─ BP_Enemy_Shade × 1 (pursues in void — cannot be killed) + └─ BP_Checkpoint_VoidThreshold (one-time checkpoint) + +Shrine Solution: + ├─ Shrine 1: Player's reflection shows a key moment from earlier + ├─ Shrine 2: Voice whispers a memory fragment + ├─ Shrine 3: Touch the light → all shrines activate → exit opens + └─ Timer: 5 minutes. If exceeded → Shade catches → respawn at shrine 1 +``` + +--- + +## L9 — L_Ending_Truth (Cinematic) + +**Duration:** ~5 minutes | **Access:** Auto-transition from WardensOffice + +``` +Setup: + ├─ Empty dark room with single point of light + ├─ Camera sequence (Sequencer): + │ ├─ Ending A (Resist): Player runs through collapsing asylum halls + │ │ └─ Bursts through front doors → daylight → freedom + │ ├─ Ending B (Merge): Entity possesses player + │ │ └─ Asylum calms. Patient enemies bow. Player's eyes glow. + │ └─ Ending C (Sacrifice): Player walks into void + │ └─ Light fades to black. Credits music begins. + │ + └─ On cutscene end: TransitionToChapter(Credits) +``` + +--- + +## L10 — L_Credits + +**Duration:** ~1-2 minutes or skip + +``` +Setup: + ├─ WBP_CreditsScreen with scroll animation + ├─ Background: black with void particles + ├─ Audio: Credits music (ending-specific variant) + │ + ├─ Post-credit: WBP_DeathScreen overlay + │ └─ Shows: + │ ├─ "Ending Achieved: [Name]" + │ ├─ Total Deaths: X + │ ├─ Total Playtime: HH:MM:SS + │ ├─ Collectibles: Y/15 + │ ├─ Enemies Defeated: Z + │ └─ Playstyle: Aggressive/Stealthy/Explorer/Balanced + │ + └─ Buttons: + ├─ "Return to Main Menu" → L_MainMenu + └─ "New Game+" → Set NG+ flag → restart with harder difficulty +``` + +--- + +## L11 — L_Test_Sandbox (Development) + +**Purpose:** Quick testing of all systems. NOT included in shipping build. + +``` +Layout (open test area): + ┌──────────────────────────────────────────┐ + │ SPAWN ZONE │ + │ ├─ PlayerStart │ + │ ├─ BP_Checkpoint_SafeRoom (reset) │ + │ └─ WBP_DebugMenu accessible │ + │ │ + │ ITEM ZONE │ + │ ├─ All 18 BP_Pickup_* actors │ + │ └─ Labelled with floating text │ + │ │ + │ WEAPON ZONE │ + │ ├─ All 4 held weapon actors on racks │ + │ └─ Infinite ammo crates │ + │ │ + │ ENEMY ZONE │ + │ ├─ BP_Enemy_Patient (idle) │ + │ ├─ BP_Enemy_Orderly (idle) │ + │ ├─ BP_Enemy_Shade (idle) │ + │ └─ Buttons: "Alert All", "Reset All" │ + │ │ + │ DOOR ZONE │ + │ ├─ All 4 door types │ + │ └─ Keys placed nearby │ + │ │ + │ CONTAINER ZONE │ + │ ├─ BP_Chest_Supply, Desk_Drawer, │ + │ │ FilingCabinet │ + │ └─ Pre-populated with test items │ + │ │ + │ PUZZLE ZONE │ + │ ├─ Fusebox, Pipe Organ, Morgue Drawers │ + │ └─ Solution hints on wall │ + │ │ + │ ATMOSPHERE ZONE │ + │ ├─ Room audio zone test trigger │ + │ ├─ Light event test panel │ + │ ├─ Scare event buttons │ + │ └─ Stress manipulation slider │ + │ │ + │ UI ZONE │ + │ ├─ All WBP_ widgets testable │ + │ └─ Notification toast test panel │ + └──────────────────────────────────────────┘ + +Setup: + ├─ In World Settings: GameMode = GM_HorrorGameMode + ├─ Auto-enable BPC_DevCheatManager on spawn + ├─ WBP_DebugMenu toggled via F1 key + └─ Exclude from shipping build via "Never Cook" directory flag +``` + +--- + +## Level Creation Checklist (per level) + +For each level, ensure: + +- [ ] **World Settings** → GameMode Override = `GM_HorrorGameMode` +- [ ] **PlayerStart** actor placed (at least one) +- [ ] **Lighting**: DirectionalLight + SkyLight + PostProcessVolume (horror grade) +- [ ] **Audio**: At least one `BP_RoomAudioZone` covering the playable area +- [ ] **Navigation Mesh**: Build → Build Paths (NavMeshBoundsVolume covering all walkable areas) +- [ ] **Collision**: All walls/floors have collision; doors have interaction collision +- [ ] **Loading**: Level name registered in `GM_HorrorGameMode.HorrorLevelNames` map +- [ ] **Save Support**: All `I_Persistable` actors present (doors, pickups, enemies) +- [ ] **Checkpoint**: At least one `BP_Checkpoint` placed +- [ ] **Level Blueprint** → OnBeginPlay: + - Set chapter tag on `GS_HorrorGameState` + - Load atmosphere profile via `BPC_AtmosphereStateController` + - Start music via `SS_AudioManager` + - Fire `BPC_NarrativeStateSystem.OnChapterStart` + +--- + +## Notes for Expansion + +- Each level should have a **paper map** (diegetic) that player can find — shows layout +- Consider **streaming sub-levels** for large areas (basement corridors) to manage memory +- Add **environmental storytelling**: blood trails, scratch marks, patient drawings on walls +- The void space could be **procedurally generated** using `BPC_AdaptiveEnvironmentDirector` +- Add **New Game+ variations**: enemy placements shift, doors start locked differently +- Test sandbox should include a **stress/sanity debug overlay** (F1 toggle) +- Each level exit door should glow faintly (diegetic guidance, not quest marker) +- Use **data layers** for adaptive environment: rooms that shift should be in a separate data layer + +--- + +*Level Design for Project Void. See [GAMEINDEX.md](GAMEINDEX.md) for full game structure. See [scene-flow.md](scene-flow.md) for transition wiring.* diff --git a/docs/game/narrative-progression.md b/docs/game/narrative-progression.md new file mode 100644 index 0000000..7ec1dac --- /dev/null +++ b/docs/game/narrative-progression.md @@ -0,0 +1,354 @@ +# Narrative & Progression — Objectives, Dialogue, Cutscenes, Endings + +**Game:** Project Void | **Build Phase:** 13 +**Framework Systems:** 58_BPC_NarrativeStateSystem, 59_BPC_ObjectiveSystem, 60_BPC_DialoguePlaybackSystem, 61_BPC_DialogueChoiceSystem, 62_BPC_BranchingConsequenceSystem, 63_BPC_TrialScenarioSystem, 64_BPC_CutsceneBridge, 65_BPC_LoreUnlockSystem, 66_DA_NarrativeDataAssets, 67_BP_NarrativeTriggerVolume, 68_BPC_EndingAccumulator + +--- + +## Purpose + +Defines the complete narrative structure: objectives, dialogue, cutscenes, lore, and the multiple-ending system. Every narrative system from the framework is demonstrated. + +--- + +## Objective Flow + +### Main Objectives (Required for Completion) + +``` +OBJ_1: "Find a way out of this wing" + ├─ Chapter: Entry + ├─ Trigger: After opening cutscene + ├─ Completion: Reach Nurses Station → interact with exit door + └─ Reward: Unlocks WardA + first checkpoint + +OBJ_2: "Restore power to the asylum" + ├─ Chapter: WardA + ├─ Trigger: Enter WardA + ├─ Sub-objectives: + │ ├─ "Find the fusebox" → Locate utility closet + │ ├─ "Reset the circuit breakers" → Solve fusebox puzzle + │ └─ "Access the treatment room" → Unlocked door + ├─ Completion: Fusebox puzzle solved + door opened + └─ Reward: Treatment room access (lore + photo collectible) + +OBJ_3: "Find the Warden's Key" + ├─ Chapter: WardB → Basement + ├─ Trigger: Read patient file mentioning "Warden's Master Key" + ├─ Sub-objectives: + │ ├─ "Find the morgue" → Navigate WardB to morgue + │ ├─ "Search the morgue drawers" → Solve morgue puzzle + │ └─ "Retrieve the key" → Open correct drawer + ├─ Completion: Morgue puzzle solved + └─ Reward: Warden's Key + Void Shard (crematorium access) + +OBJ_4: "Confront the truth" + ├─ Chapter: WardensOffice + ├─ Trigger: Enter Warden's Office + ├─ Sub-objectives: + │ ├─ "Read the Warden's journal" → Find and read journal + │ └─ "Face the entity" → Trigger dialogue choice + ├─ Completion: Dialogue choice made + └─ Reward: Ending triggered +``` + +### Optional / Side Objectives + +``` +SIDE_1: "Find the survivors" + ├─ Trigger: Find first Patient File mentioning "Subject 77 escaped" + ├─ Objective: Locate BP_NPC_Survivor in WardB + └─ Reward: Lore, possible ending flag (SurvivorSaved), item gift + +SIDE_2: "Collect the memories" + ├─ Trigger: Find first Old Photo + ├─ Objective: Find all 4 Old Photos + └─ Reward: Achievement "Photographer", lore about asylum history + +SIDE_3: "Touch the void" + ├─ Trigger: Find first Void Shard + ├─ Objective: Collect all 3 Void Shards + └─ Reward: Unlocks Ending C (Sacrifice), achievement "Void Touched" +``` + +--- + +## Objective Data Assets + +Each objective has a `DA_ObjectiveData` (123): + +| DA_Objective_* | Type | Prerequisites | Completion | Chapter | +|----------------|------|--------------|-----------|---------| +| `DA_Objective_FindKeys` | Main | None | Has item: Keycard Alpha | WardA | +| `DA_Objective_RestorePower` | Main | OBJ_1 complete | Flag: PowerRestored | WardA | +| `DA_Objective_FindWardenKey` | Main | OBJ_2 complete | Has item: Warden's Key | Basement | +| `DA_Objective_ConfrontTruth` | Main | OBJ_3 complete | DialogueChoice made | WardensOffice | +| `DA_Objective_FindSurvivors` | Side | Find PatientFile #2 | NPC Interacted | WardB | +| `DA_Objective_CollectMemories` | Side | Find Photo #1 | All 4 photos found | Any | +| `DA_Objective_TouchVoid` | Side | Find Shard #1 | All 3 shards found | Any | + +--- + +## Dialogue System + +### Dialogue Data Assets (DA_Dialogue_*) + +| DA_Dialogue_ | Speaker | Lines | Location | +|-------------|---------|:---:|----------| +| `DA_Dialogue_Intro` | Player (internal monologue) | 3 lines | L_Asylum_Entry — wake up | +| `DA_Dialogue_SurvivorEncounter` | NPC Survivor | 8 lines + 3 player choices | WardB — hidden room | +| `DA_Dialogue_VoidWhispers` | Void Entity | 12 lines (random) | Throughout — triggered by stress | +| `DA_Dialogue_EndingTruth` | Void Entity | 15 lines + 3 player choices | WardensOffice — final | + +### Dialogue Playback (BPC_DialoguePlaybackSystem) + +``` +PlayDialogue(DialogueData) + │ + ├─ [Queue Lines] + │ └─ For each line in DialogueData.Lines: + │ ├─ Show speaker name (WBP_AccessibilityUI) + │ ├─ Play VO audio (SS_AudioManager.PlayDialogue) + │ ├─ Show subtitle text + │ ├─ Wait(DialogueData.LineDuration) + │ └─ Auto-advance to next line + │ + ├─ [On Line Change] + │ ├─ BPC_LoreUnlockSystem → auto-unlock if line contains lore reference + │ └─ WBP_NotificationToast → "New Lore Entry: [Name]" if lore unlocked + │ + └─ [On Dialogue Complete] + ├─ If has choices → BPC_DialogueChoiceSystem.PresentChoices() + └─ If no choices → Broadcast OnDialogueCompleted +``` + +### Dialogue Choices (BPC_DialogueChoiceSystem) + +Example: Final confrontation — "Accept / Resist / Sacrifice" + +``` +PresentChoices(ChoiceSet, Options[]) + │ + ├─ [Display] Show 3 buttons on WBP_JournalHorror: + │ ├─ "Accept the entity's offer" (Option A) + │ ├─ "Resist and fight" (Option B) + │ └─ "Sacrifice yourself to seal it" (Option C) + │ + ├─ [Conditions] Show/hide options based on flags: + │ ├─ Option C (Sacrifice) → requires all 3 VoidShards collected + │ ├─ Option A (Accept) → requires Sanity < 50% (broken enough) + │ └─ Option B (Resist) → always available + │ + ├─ [Player Selects] + │ │ + │ ├─ BPC_BranchingConsequenceSystem.ExecuteConsequence(Choice) + │ │ ├─ Record choice on PS_HorrorPlayerState + │ │ ├─ Set narrative flag: "ChoseAccept" / "ChoseResist" / "ChoseSacrifice" + │ │ └─ BPC_EndingAccumulator.SubmitChoice(Choice) + │ │ + │ └─ Play consequence dialogue/cutscene + │ + └─ [Consequence Results] + ├─ Accept → Entity possesses player → Ending B + ├─ Resist → Entity attacks → Chase sequence → Ending A + └─ Sacrifice → Entity sealed → Player dies → Ending C +``` + +--- + +## Cutscene System (BPC_CutsceneBridge) + +### Cutscenes + +| DA_Cutscene_ | Type | Duration | Trigger | +|-------------|------|:---:|---------| +| `DA_Cutscene_Opening` | In-engine | 5s | OnPostLogin in L_Asylum_Entry | +| `DA_Cutscene_ShadeFirstAppearance` | In-engine | 8s | Enter WardB courtyard | +| `DA_Cutscene_VoidFirstShift` | In-engine | 12s | Enter WardB morgue | +| `DA_Cutscene_Ending` | Pre-rendered or in-engine | 30-60s | Dialogue choice resolution | + +### Cutscene Bridge Flow + +``` +PlayCutscene(CutsceneData) + │ + ├─ [Pre-Cutscene] + │ ├─ BPC_StateManager.ForcePushState(Cutscene) + │ ├─ SS_EnhancedInputManager.PopAllContexts → PushContext(UI) + │ └─ WBP_HUDController.HideAllDiegetic() + │ + ├─ [Play] + │ ├─ If CutsceneData.Type == InEngine: + │ │ ├─ Spawn cutscene actors from CutsceneData.ActorList + │ │ ├─ Sequencer → Play Level Sequence + │ │ └─ Camera transfer → BPC_CameraStateLayer.SetSequenceCamera() + │ │ + │ └─ If CutsceneData.Type == PreRendered: + │ ├─ Load video file + │ └─ Play on full-screen media texture + │ + ├─ [Skip Detection] + │ ├─ Listen for IA_SkipCutscene (Hold for 1.5s) + │ └─ OR: Any key press if CutsceneData.bAllowSkip + │ + ├─ [Post-Cutscene] + │ ├─ BPC_StateManager.RestorePreviousState() + │ ├─ Restore input context + │ ├─ BPC_NarrativeStateSystem.SetFlag(CutsceneData.CompletionFlag) + │ └─ WBP_HUDController.ShowAllDiegetic() + │ + └─ Broadcast OnCutsceneCompleted +``` + +--- + +## Lore System (BPC_LoreUnlockSystem) + +Lore entries unlock as the player discovers documents, patient files, and experiences events: + +| Lore Entry | Unlock Method | Category | +|-----------|-------------|----------| +| "The Founding of Blackwood" | Read Journal Page #1 | History | +| "Patient Zero" | Read Patient File #3 | Patients | +| "The Void Breach" | Experience first void shift | Events | +| "Dr. Blackwood's Descent" | Read Warden's Journal | Characters | +| "The Entity's Origin" | Collect all 3 VoidShards | Secrets | +| "Treatment Methods" | Read Patient File #2 | Patients | +| "Asylum Layout" | Find Map item (optional) | World | + +Each unlocked entry: +- Appears in WBP_JournalHorror → "Lore" tab +- Grants small stress reduction (-5) when read (understanding = comfort) +- May contain clues for puzzles (e.g., morgue drawer order hidden in Patient File) + +--- + +## Ending Accumulator (BPC_EndingAccumulator) + +The system that determines which ending the player receives. + +### Input Factors + +| Factor | Source | Possible Values | +|--------|--------|----------------| +| **VoidShards collected** | BPC_CollectibleTracker | 0, 1, 2, 3 | +| **Survivor saved** | Narrative flag | true / false | +| **Final Sanity** | PS_HorrorPlayerState | 0-100 (Low <33, Med 33-66, High >66) | +| **Dialogue choice** | BPC_DialogueChoiceSystem | Accept / Resist / Sacrifice | +| **Playstyle** | BPC_PlayerMetricsTracker | Aggressive / Stealthy / Balanced | +| **Enemies killed** | BPC_ProgressStatTracker | Count (0, 1-5, 6-15, 16+) | + +### Ending Determination + +``` +EvaluateEnding(FinalChoice) + │ + ├─ [Check Sacrifice Qualification] + │ └─ FinalChoice == Sacrifice AND VoidShards >= 3? + │ ├─ True → ENDING C: "The Seal" (Sacrifice) + │ └─ False → "You lack the void-touched knowledge to seal it." + │ └─ Force player to choose Accept or Resist + │ + ├─ [Evaluate Accept vs Resist] + │ │ + │ ├─ Score "Accept" based on: + │ │ ├─ VoidShards (×10 each) + │ │ ├─ Sanity < 50 (+20) + │ │ ├─ Playstyle == Aggressive (+10) + │ │ └─ EnemiesKilled > 10 (+10) + │ │ + │ ├─ Score "Resist" based on: + │ │ ├─ Sanity > 50 (+20) + │ │ ├─ SurvivorSaved (+25) + │ │ ├─ Playstyle == Stealthy (+10) + │ │ └─ EnemiesKilled == 0 (+15 — Pacifist bonus) + │ │ + │ └─ Higher score wins: + │ ├─ Accept > Resist → ENDING B: "The Merge" (Become) + │ └─ Resist ≥ Accept → ENDING A: "The Escape" (Flee) + │ + ├─ [Record Result] + │ ├─ GS_HorrorGameState → SetGlobalFlag("EndingAchieved", EndingTag) + │ ├─ BPC_ProgressStatTracker.RecordEnding(EndingTag) + │ └─ Broadcast OnEndingDetermined(EndingTag) + │ + └─ → GM_HorrorGameMode.TriggerEnding(EndingTag) +``` + +### Ending Variants Summary + +| Ending | Name | Conditions | Cutscene Description | +|--------|------|-----------|---------------------| +| **A** | "The Escape" | Resist + low void influence | Player flees collapsing asylum, emerges into daylight. Free, but haunted. | +| **B** | "The Merge" | Accept + high void influence | Entity merges with player. Asylum calms. Patients bow. Player's eyes glow void-purple. | +| **C** | "The Seal" | Sacrifice + 3 VoidShards | Player walks into void. Light fades. Entity sealed. Player's sacrifice remembered in credits. | + +--- + +## Narrative Trigger Volumes (BP_NarrativeTriggerVolume) + +| Volume | Level | Trigger | Action | +|--------|-------|---------|--------| +| `BP_NarrativeTrigger_Intro` | L_Asylum_Entry | OnBeginOverlap (player) | Play opening cutscene | +| `BP_NarrativeTrigger_WardA_Enter` | L_Asylum_WardA | OnBeginOverlap | Set flag "WardA_Entered", update objective | +| `BP_NarrativeTrigger_VoidFirst` | L_Asylum_WardB (morgue) | OnBeginOverlap | Trigger first void shift event | +| `BP_NarrativeTrigger_ShadeAppear` | L_Asylum_WardB (courtyard) | OnBeginOverlap | Spawn Shade teaser | +| `BP_NarrativeTrigger_Ending` | L_Asylum_WardensOffice | OnBeginOverlap | Start final dialogue + ending evaluation | + +--- + +## Narrative Flow Diagram + +``` +Player → ENTRY CHAPTER + │ "Dr. Eliza Vance wakes in a padded cell." + │ "Her memory is fragmented. Voices whisper from the dark." + │ + ▼ +Player → WARDA CHAPTER + │ "The asylum is a maze. Patients roam, their minds hollowed." + │ "A security office. A pistol. A guarded corridor." + │ "A nurse's station. A keycard. A way forward." + │ + ▼ +Player → WARDB CHAPTER + │ "The high-security ward. Orderlies patrol. Something watches." + │ "A morgue. A shotgun. A void that shifts reality." + │ "A courtyard. A shadow that moves wrong. First contact." + │ "A chapel. An organ. A melody that opens sealed doors." + │ + ▼ +Player → BASEMENT CHAPTER + │ "Absolute darkness. The Shade hunts. Every sound is death." + │ "Morgue drawers. A puzzle. A key belonging to the warden." + │ "A crematorium. A void shard. Memories of fire." + │ + ▼ +Player → WARDEN'S OFFICE + │ "The warden's final journal. The truth laid bare." + │ "The entity speaks through the mirror." + │ "A choice that determines everything." + │ + ▼ + ┌─────────┼─────────┐ + ▼ ▼ ▼ +ENDING A ENDING B ENDING C + Escape Merge Sacrifice +``` + +--- + +## Notes for Expansion + +- Add **branching mid-game**: saving survivor opens alternate route through WardB +- Add **multiple NPC dialogues**: more survivors with different agendas +- Add **mystery documentation**: some patient files have blacked-out text → find "decoder lens" item +- Add **trial scenarios**: timed escape sequences where player must reach exit before Shade +- Add **New Game+ lore**: after first completion, new documents appear that explain the entity's origin +- Add **voice acting** support: hook VO audio clips to dialogue lines +- Add **dialogue history**: player can review past dialogue in journal +- Multiplayer: narrative flags must be server-authoritative and replicated to joining clients + +--- + +*Narrative & Progression for Project Void. See [GAMEINDEX.md](GAMEINDEX.md) for full game structure. See [07-narrative/](../blueprints/07-narrative/) for framework narrative system specs.* diff --git a/docs/game/playercharacter.md b/docs/game/playercharacter.md new file mode 100644 index 0000000..510c446 --- /dev/null +++ b/docs/game/playercharacter.md @@ -0,0 +1,445 @@ +# 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.* diff --git a/docs/game/polish-loading-credits.md b/docs/game/polish-loading-credits.md new file mode 100644 index 0000000..8dadea6 --- /dev/null +++ b/docs/game/polish-loading-credits.md @@ -0,0 +1,354 @@ +# Polish — Tutorials, Loading, Credits, Debug, Analytics + +**Game:** Project Void | **Build Phases:** 19, 20 +**Framework Systems:** 106_BPC_AnalyticsTracker, 107_BPC_DevCheatManager, 108_BPC_ErrorHandler, 109_BPC_FPSCounter, 110_BPC_LoadingScreen, 111_BPC_TutorialSystem, 112_WBP_CreditsScreen, 113_WBP_DebugMenu, 114_WBP_SplashScreen + +--- + +## Purpose + +Covers all polish systems: tutorial triggers, loading screen, credits, debug tools, analytics, and error handling. These are finalized after core gameplay is working. + +--- + +## Tutorial System (BPC_TutorialSystem) + +### Tutorial Triggers + +| Trigger Actor | Level | Condition | Teaches | +|--------------|-------|-----------|---------| +| `BP_TutorialTrigger_Controls` | L_Asylum_Entry | OnBeginPlay | Move WASD + Mouse Look | +| `BP_TutorialTrigger_Interact` | L_Asylum_Entry | OnOverlap with first item | Press E to interact | +| `BP_TutorialTrigger_Crouch` | L_Asylum_Entry | OnOverlap near vent | Press C to crouch through vents | +| `BP_TutorialTrigger_Hiding` | L_Asylum_Entry | OnOverlap near locker | Press E on hiding spots | +| `BP_TutorialTrigger_Combat` | L_Asylum_WardA | OnEquip first weapon | Fire/Reload/Aim basics | +| `BP_TutorialTrigger_Battery` | L_Asylum_Basement | OnOverlap in first dark room | Battery management for flashlight | + +### Tutorial Prompt Behavior + +``` +BP_TutorialTrigger_* → Event BeginOverlap (Player) + │ + ├─ [Already Shown?] + │ └─ BPC_TutorialSystem.HasSeenTutorial(TutorialTag)? → Return + │ + ├─ [Show Tutorial] + │ ├─ WBP_NotificationToast (special tutorial variant): + │ │ ├─ Large prompt text (e.g., "Press E to interact with objects") + │ │ ├─ Key icon (E key or gamepad X/Square icon) + │ │ ├─ Timer: 10 seconds or until condition met + │ │ └─ Position: Top-center (not to block crosshair) + │ │ + │ ├─ [Pause hint] + │ │ ├─ Dim surrounding screen slightly + │ │ └─ Highlight the interaction target (glow outline) + │ │ + │ └─ BPC_TutorialSystem.MarkTutorialSeen(TutorialTag) + │ └─ Won't show again this save slot + │ + └─ [Dismiss Conditions] + ├─ Player performs the action → auto-dismiss + ├─ Player moves away from trigger → dismiss after 2s + └─ Timer expires (10s) → auto-dismiss +``` + +--- + +## Loading Screen (BPC_LoadingScreen + WBP_LoadingHorror) + +### Full Loading Flow + +``` +GI_GameFramework.SetGamePhase(Loading) + │ + ├─ [Pre-Load] + │ ├─ BPC_LoadingScreen.Activate() + │ ├─ WBP_LoadingHorror.AddToViewport(ZOrder = 100 — above everything) + │ ├─ SS_AudioManager.PlaySFX("loading_start") + │ └─ Set background image based on destination chapter + │ + ├─ [During Load] + │ ├─ Tick → GetAsyncLoadPercentage() → UpdateProgressBar + │ │ └─ Smooth interpolation (lerp to actual value) + │ ├─ Tip rotation: every 8 seconds, show random tip + │ ├─ Chapter title: "Ward A — Patient Wing" + │ └─ Audio: loading ambient loop plays + │ + └─ [On Level Loaded] + ├─ Progress bar completes (snap to 100%) + ├─ Brief pause (0.5s) so player sees complete bar + ├─ SS_AudioManager.StopSFX("loading_start") + ├─ WBP_LoadingHorror.RemoveFromParent() + ├─ BPC_LoadingScreen.Deactivate() + └─ GI_GameFramework.SetGamePhase(InGame) +``` + +### Loading Tips (DT_LoadingTips) + +| Category | Tip | +|----------|-----| +| **Stealth** | "Darkness is your ally. Enemies can't see what they can't hear." | +| **Resource** | "Batteries are scarce. Use the flashlight sparingly." | +| **Exploration** | "Check every drawer. The asylum hides its secrets well." | +| **Survival** | "Your sanity is as important as your health. Find pills." | +| **Combat** | "Some enemies fear the light. Others are drawn to it." | +| **Lore** | "The void watches. It remembers. It waits." | +| **Puzzle** | "Not all doors need a key. Some need a different opening." | +| **Movement** | "Running attracts attention. Sometimes walking is survival." | +| **Inventory** | "Key items cannot be lost. They persist through death." | +| **Atmosphere** | "Listen. The asylum speaks to those who pay attention." | + +--- + +## Credits Screen (WBP_CreditsScreen) + +### Credits Layout + +``` +┌──────────────────────────────────────────────┐ +│ │ +│ PROJECT VOID │ +│ ═══════════════════════ │ +│ │ +│ A Psychological Horror Experience │ +│ │ +│ ──────── DEVELOPMENT ──────── │ +│ Game Director: [Name] │ +│ Lead Programmer: [Name] │ +│ Systems Designer: [Name] │ +│ Level Designer: [Name] │ +│ Narrative Designer: [Name] │ +│ Audio Designer: [Name] │ +│ Environment Artist: [Name] │ +│ Character Artist: [Name] │ +│ UI/UX Designer: [Name] │ +│ QA Lead: [Name] │ +│ │ +│ ──────── SPECIAL THANKS ──────── │ +│ UE5 Modular Game Framework Team │ +│ GASP Animation System │ +│ ... │ +│ │ +│ ──────── POWERED BY ──────── │ +│ Unreal Engine 5 │ +│ │ +│ © 2026 [Studio Name]. All rights reserved.│ +│ "In the void, memory is currency." │ +│ │ +│ [SKIP — ESC] [RETURN TO MENU — ENTER] │ +└──────────────────────────────────────────────┘ +``` + +### Credits Flow + +``` +L_Credits.OnBeginPlay + │ + ├─ WBP_CreditsScreen.AddToViewport() + ├─ Start scroll animation (Timeline: verticalTranslation, duration 45s) + ├─ SS_AudioManager.PlayMusic("credits_theme", MS_MusicBus) + │ + ├─ [Skip] + │ └─ IA_SkipCutscene (Hold 1.5s) → jump to end + │ + ├─ [On Complete] + │ ├─ Show post-credit stats (WBP_DeathScreen variant) + │ ├─ Achievement unlocks fire + │ └─ Buttons: "Main Menu" / "New Game+" + │ + └─ On Return to Menu → OpenLevel("L_MainMenu") +``` + +--- + +## Debug Tools (BPC_DevCheatManager + WBP_DebugMenu) + +### Dev Cheats (BPC_DevCheatManager) + +| Cheat | Key | Effect | +|-------|-----|--------| +| God Mode | `F1 → Toggle` | Infinite health, no damage | +| Noclip | `F2` | Fly through walls | +| Give Item | `F3 → type item tag` | Adds any item to inventory | +| Teleport | `F4 → click on level` | Teleport to cursor location | +| Toggle AI | `F5` | Freeze/unfreeze all AI | +| Toggle HUD | `F6` | Show/hide HUD for screenshots | +| Spawn Enemy | `F7 → select type` | Spawn test enemy at cursor | +| Stress Slider | `F8 → mouse wheel` | Adjust player stress 0-100 | +| Skip Chapter | `F9 → select chapter` | Jump to any level | +| Reload Level | `F10` | Quick level restart | +| Toggle Debug | `F11` | Show AI paths, perception, stress values | + +### WBP_DebugMenu (toggle via F11 or console command) + +``` +Debug Menu Tabs: + ├─ [State Viewer] + │ ├─ Current Game Phase + │ ├─ Player Health / Stamina / Stress / Sanity + │ ├─ Active chapter + │ ├─ Current BPC_StateManager state + │ ├─ All narrative flags (map view) + │ └─ Active objectives + │ + ├─ [Inventory] + │ ├─ Grid view of all items + │ ├─ Add/Remove buttons + │ └─ Weight/space display + │ + ├─ [AI] + │ ├─ List all active enemies + states + │ ├─ Toggle AI perception debug (cones + hearing radius) + │ ├─ Toggle patrol path display + │ └─ Force alert/calm all enemies + │ + ├─ [Performance] + │ ├─ FPS counter (min/max/avg) + │ ├─ Memory usage + │ ├─ Draw calls + │ └─ Actor count + │ + ├─ [Audio] + │ ├─ Bus volume sliders (live) + │ ├─ Active room zone + │ └─ Playing sounds list + │ + └─ [Cheats] (if enabled) + ├─ God Mode, Noclip toggles + ├─ Give Item dropdown + ├─ Teleport to cursor + └─ Skip Chapter dropdown +``` + +--- + +## Error Handler (BPC_ErrorHandler) + +``` +BPC_ErrorHandler — catches and handles errors gracefully + +OnError(ErrorMessage, ErrorSeverity) + │ + ├─ Severity == Warning: + │ ├─ FL_GameUtilities.LogWarning(ErrorMessage) + │ └─ WBP_NotificationToast.Show("Warning: " + ErrorMessage, 5s) + │ + ├─ Severity == Error: + │ ├─ FL_GameUtilities.LogError(ErrorMessage) + │ ├─ WBP_NotificationToast.Show("Error: " + ErrorMessage, 10s) + │ └─ (Continue execution if recoverable) + │ + └─ Severity == Critical: + ├─ FL_GameUtilities.LogCritical(ErrorMessage) + ├─ BPC_AnalyticsTracker.LogCrash(ErrorMessage, StackTrace) + ├─ SS_SaveManager.QuickSave("CrashRecovery") + ├─ Show error screen: + │ ├─ "A critical error occurred." + │ ├─ "Your progress has been saved." + │ ├─ [Return to Main Menu] + │ └─ [Send Error Report] (if analytics enabled) + └─ OpenLevel("L_MainMenu") (safe fallback) +``` + +--- + +## FPS Counter (BPC_FPSCounter) — Dev Only + +``` +BPC_FPSCounter — Simple FPS display in top-right corner + +Config: + ├─ bShowFPS: Bool (default: false — enable via debug menu or -showfps launch arg) + ├─ bShowMinMax: Bool + ├─ UpdateInterval: Float (0.5s) + +Display Format: + "FPS: 60 | Min: 42 | Max: 120" + +Colors: + ├─ FPS >= 60: Green + ├─ FPS 30-59: Yellow + └─ FPS < 30: Red + +Launch Arg: -ShowFPS → auto-enables on boot +``` + +--- + +## Analytics Tracker (BPC_AnalyticsTracker) — Opt-in + +``` +BPC_AnalyticsTracker tracks session metrics: + +Session Events: + ├─ GameStart (timestamp, platform, settings) + ├─ GameEnd (ending, playtime, deaths, timestamp) + ├─ ChapterEnter (chapter tag, timestamp) + ├─ ChapterComplete (chapter tag, time spent, deaths during chapter) + ├─ PlayerDeath (cause, location, chapter, time in chapter) + ├─ ItemPickup (item tag, location, chapter) + ├─ WeaponUsed (weapon tag, shots fired, hits, misses) + ├─ PuzzleSolved (puzzle tag, time spent, hints used) + ├─ ScareExperienced (scare tag, location) + ├─ MenuOpen (menu type, duration) + ├─ Crash (error message, stack trace, last 10 events) + └─ SettingsChanged (setting name, old value, new value) + +Privacy: + ├─ All analytics are OPT-IN (prompt on first launch) + ├─ No PII collected (no usernames, IP addresses) + ├─ Data stored locally, sent only if player enables + └─ SS_SettingsSystem stores analytics consent +``` + +--- + +## Blueprint Wiring Checklist + +### Tutorials +- [ ] Create 6 `BP_TutorialTrigger_*` actors +- [ ] Wire `BPC_TutorialSystem` with seen-tutorial tracking +- [ ] Create tutorial variant of `WBP_NotificationToast` +- [ ] Place triggers at appropriate tutorial moments in levels + +### Loading +- [ ] Wire `BPC_LoadingScreen` to game phase changes (InGame ↔ Loading) +- [ ] Create `DT_LoadingTips` Data Table with 10 tips +- [ ] Wire tip rotation + progress bar + background selection + +### Credits +- [ ] Create `L_Credits` level with dark background +- [ ] Create scroll animation for credits text +- [ ] Wire post-credit stats display + achievement checks + +### Debug +- [ ] Create `BPC_DevCheatManager` with all cheat functions +- [ ] Create `WBP_DebugMenu` with 5 tabs +- [ ] Bind cheats to F1-F11 keys (dev only, exclude from shipping) +- [ ] Add `BPC_FPSCounter` with -ShowFPS launch arg + +### Error Handling +- [ ] Wire `BPC_ErrorHandler` to catch critical errors +- [ ] Create error screen widget with Return to Menu button +- [ ] Wire crash auto-save before error screen + +### Analytics +- [ ] Wire `BPC_AnalyticsTracker` to key events (death, chapter, item, scare) +- [ ] Add analytics consent prompt on first launch +- [ ] Wire opt-in/opt-out toggle in Settings + +--- + +## Notes for Expansion + +- Add **achievement integration**: all polish systems should fire achievement checks +- Add **speedrun timer**: optional HUD element showing real-time + chapter splits +- Add **photo mode**: pause game, free camera, filters, hide HUD +- Add **benchmarking tool**: automated walkthrough to measure performance +- Add **controller remapping UI**: rebind every action via `SS_EnhancedInputManager.RebindKey()` +- Add **multi-language support**: loading tips, tutorial text, credits in localized string tables +- All debug tools and cheats must be **excluded from shipping builds** (use `#if !UE_BUILD_SHIPPING`) + +--- + +*Polish Systems for Project Void. See [GAMEINDEX.md](GAMEINDEX.md) for full game structure. See [13-polish/](../blueprints/13-polish/) for framework polish system specs.* diff --git a/docs/game/save-checkpoints.md b/docs/game/save-checkpoints.md new file mode 100644 index 0000000..c9d6e46 --- /dev/null +++ b/docs/game/save-checkpoints.md @@ -0,0 +1,387 @@ +# Save System & Checkpoints — Persistence, Death, Void Space + +**Game:** Project Void | **Build Phases:** 15 +**Framework Systems:** 35_SS_SaveManager, 36_I_Persistable, 37_BP_Checkpoint, 38_BPC_AltDeathSpaceSystem, 39_BPC_DeathHandlingSystem, 40_BPC_PersistentCorpseSystem, 41_BPC_PersistentWorldStateRecorder, 42_BPC_PlayerRespawnSystem, 43_BPC_RunHistoryTracker + +--- + +## Purpose + +Defines the complete save/load, checkpoint, death handling, and void space systems. Every framework save system is demonstrated. + +--- + +## Save Slots & Save Manager + +### SS_SaveManager Configuration + +``` +SS_SaveManager (35) — initialized by GI_HorrorGame on boot + +Save Slots: + ├─ SaveSlot_1 → AutoSave (checkpoint-based) + ├─ SaveSlot_2 → QuickSave (pause menu quick save) + ├─ SaveSlot_3 → ManualSave (pause menu manual save) + └─ (Up to 10 slots configurable via DA_SaveSettings) + +Save Structure (per slot): + ├─ SaveHeader + │ ├─ SlotName (Text) + │ ├─ TimeStamp (DateTime) + │ ├─ PlaytimeSeconds (Float) + │ ├─ ChapterTag (GameplayTag) + │ ├─ PlayerTransform (Transform) + │ ├─ Thumbnail (Texture2D*) — screenshot at save time + │ └─ VersionNumber (Int32) + │ + ├─ PlayerState (PS_HorrorPlayerState) + │ ├─ Health (Float) + │ ├─ Stamina (Float) + │ ├─ Stress (Float) + │ ├─ InventorySlots (Array) + │ ├─ AmmoPool (Map) + │ ├─ EquipmentState (Map) + │ ├─ NarrativeFlags (Map) + │ ├─ Objectives (Array) + │ ├─ DeathsThisChapter (Int32) + │ └─ FearHistory (Array) + │ + └─ WorldState (BPC_PersistentWorldStateRecorder) + ├─ DoorStates (Array) — position, locked, barricaded + ├─ PickupStates (Array) — collected or not + ├─ EnemyStates (Array) — alive/dead, position + ├─ PuzzleStates (Array) — solved/unsolved, progress + └─ GlobalFlags (Map) +``` + +### Save Trigger Points + +| Trigger | Save Type | When | +|---------|----------|------| +| `BP_Checkpoint` overlap | AutoSave (Slot 1) | Player touches checkpoint | +| `PauseMenu → Save` | ManualSave (Slot 3) | Player manually saves | +| `GM_HorrorGameMode.HandlePlayerDead` | AutoSave (Slot 1) | Death → respawn | +| `TransitionToChapter` | AutoSave (Slot 1) | Level transition | +| `GI_HorrorGame.QuitToDesktop` | QuickSave (Slot 2) | Clean shutdown | +| `SS_SettingsSystem.OnSettingsChanged` | — | Settings saved separately (not slot-based) | + +--- + +## Checkpoint System + +### BP_Checkpoint Actors + +| Checkpoint | Level | Location | Features | +|-----------|-------|----------|----------| +| `BP_Checkpoint_SafeRoom` | Entry, WardA, WardB, Basement | Safe rooms | Full restore, safe zone, stress decay | +| `BP_Checkpoint_Mirror` | WardA (Nurses Station) | Thematic | Also stores reflection (visual) | +| `BP_Checkpoint_BasementGate` | Basement | Before Shade zone | Warning: "Danger ahead" | +| `BP_Checkpoint_VoidThreshold` | Void Space | One-time save | Disappears after first use | + +### Checkpoint Blueprint Flow + +``` +BP_Checkpoint — Event ActorBeginOverlap (Player) + │ + ├─ [Already Activated?] + │ └─ bActivated AND not bReusable? → Return + │ + ├─ [Activation] + │ ├─ Set bActivated = true + │ ├─ Play activation effect: + │ │ ├─ Light pulse (warm amber glow) + │ │ ├─ Sound: soft chime + │ │ └─ Particle: floating dust motes + │ │ + │ ├─ [Save State] + │ │ ├─ SS_SaveManager.CreateAutoSave(SlotName = ChapterTag) + │ │ ├─ BPC_PersistentWorldStateRecorder.SaveWorldState() + │ │ └─ Store player transform + all state data + │ │ + │ ├─ [Restore Player] + │ │ ├─ BPC_HealthSystem.RestoreFullHealth() + │ │ ├─ BPC_StaminaSystem.RestoreFullStamina() + │ │ └─ (Stress is NOT restored — checkpoint only saves, doesn't heal stress) + │ │ + │ ├─ [Notify Player] + │ │ ├─ WBP_NotificationToast.Show("Checkpoint Reached") + │ │ ├─ WBP_ScreenEffectController.PulseVignette(0.3s) + │ │ └─ SS_AudioManager.PlaySFX("checkpoint") + │ │ + │ └─ [Update Run History] + │ ├─ BPC_RunHistoryTracker.RecordCheckpoint(ChapterTag) + │ └─ Set GS_HorrorGameState → SaveGlobalFlag("Checkpoint_Entered", true) + │ + └─ (Safe Room only) → BPC_StressSystem.StartRapidStressDecay() + └─ 5× normal decay rate while in safe room +``` + +--- + +## Death Handling System + +### Death Flow (Complete) + +``` +BPC_HealthSystem.Health <= 0 + │ + ├─ BPC_DeathHandlingSystem.TriggerDeath(DeathCause, Killer) + │ + ├─ [Immediate Effects] + │ ├─ BPC_StateManager.ForcePushState(Death) + │ ├─ Play death animation montage (GASP) + │ │ └─ Duration: 2.0 seconds + │ ├─ BPC_CameraStateLayer.SetDeathCamera() + │ │ └─ Camera falls to floor, tilts, fades to black + │ ├─ WBP_ScreenEffectController.PlayDeathFade() + │ │ └─ Red vignette fades in → black + │ └─ SS_AudioManager.TriggerDeathAudio(DeathCause) + │ ├─ SFX: heartbeat flatline + │ ├─ Music: fade out over 2s + │ └─ Ambience: dampen to near-silence + │ + ├─ [Death Classification] + │ │ + │ ├─ Determine DeathCause from damage info: + │ │ ├─ "Physical" → Patient/Orderly melee + │ │ ├─ "Void" → Shade touch, void fall + │ │ ├─ "Environmental" → Falling debris, trap + │ │ └─ "Self" → (future: suicide? sanity loss?) + │ │ + │ ├─ DeathCause == "Void" AND NOT GI_HorrorGame.GetSessionFlag("VoidSpaceVisited")? + │ │ ├─ True → [VOID DEATH] — redirect to void space (see below) + │ │ └─ False → [NORMAL DEATH] — respawn at checkpoint + │ │ + │ └─ [Normal Death → Respawn] + │ │ + │ ├─ Increment PS_HorrorPlayerState.DeathsThisChapter + │ ├─ Increment GI_HorrorGame.TotalDeaths + │ ├─ BPC_RunHistoryTracker.LogDeath(DeathCause, Location, Time) + │ │ + │ ├─ BPC_PersistentCorpseSystem.LeaveCorpse(Location, DeathCause) + │ │ └─ Spawns ragdoll corpse at death location + │ │ └─ Persists until level transition or save clear + │ │ + │ ├─ [Lose Items?] + │ │ ├─ Check DA_GameSettings.bLoseItemsOnDeath + │ │ └─ If true: remove random 1-3 non-key items from inventory + │ │ + │ ├─ [Show Death Screen] + │ │ └─ WBP_DeathScreen.ShowDeathScreen(DeathCause, bVoidQualified=false) + │ │ └─ Player clicks "Continue" → respawn + │ │ + │ ├─ [Respawn] + │ │ ├─ SS_SaveManager.LoadSaveSlot(SaveSlot_1) // auto-save + │ │ ├─ BPC_PlayerRespawnSystem.RespawnAtCheckpoint() + │ │ │ ├─ Load last checkpoint location + │ │ │ ├─ Restore health (checkpoint value, not full) + │ │ │ ├─ Restore inventory (minus death losses) + │ │ │ └─ Restore world state (doors, items, enemies) + │ │ │ + │ │ ├─ WBP_DeathScreen.HideDeathScreen() + │ │ ├─ WBP_ScreenEffectController.PlayRespawnFadeIn() + │ │ └─ BPC_StateManager.RestorePreviousState() + │ │ + │ └─ BPC_NarrativeStateSystem.SetFlag("PlayerDied") +``` + +--- + +## Void Space / Alt Death Space + +### Entry Conditions + +``` +Player dies with DeathCause == "Void" (killed by Shade) + OR +Player interacts with void portal in Basement + AND +GI_HorrorGame.GetSessionFlag("VoidSpaceVisited") == false (first time only) + +→ BPC_AltDeathSpaceSystem.EnterVoidSpace() +``` + +### Void Space Flow + +``` +EnterVoidSpace() + │ + ├─ BPC_StateManager.ForcePushState(AltDeathSpace) + ├─ GM_HorrorGameMode → Set bVoidSpaceActive = true + ├─ GS_HorrorGameState → SetVoidActive(true) + │ + ├─ [Transition] + │ ├─ SS_SaveManager.QuickSave("PreVoid") // save real-world state + │ ├─ GM_HorrorGameMode.TransitionToChapter(Chapter.VoidSpace) + │ └─ Level loads: L_Asylum_VoidSpace + │ + ├─ [Void Space Gameplay] + │ ├─ Environment: inverted lighting, non-Euclidean geometry + │ ├─ No map/HUD compass + │ ├─ Objective: "Find the light. Escape the void." + │ │ + │ ├─ Puzzle: Activate 3 Memory Shrines + │ │ ├─ Shrine 1: Reflection shows death moment + │ │ ├─ Shrine 2: Voice whispers memory fragment + │ │ └─ Shrine 3: Touch the light + │ │ + │ ├─ Threat: Shade pursues (same entity, void-empowered) + │ │ └─ Contact = restart at Shrine 1 + │ │ + │ └─ Timer: 5 minutes (Trial Scenario System) + │ └─ If time expires: Shade becomes unavoidable → restart + │ + └─ [Exit Void Space] + │ + ├─ Player reaches exit portal (all 3 shrines activated) + │ + ├─ BPC_AltDeathSpaceSystem.ExitVoidSpace() + │ + ├─ [Return to Real World] + │ ├─ SS_SaveManager.LoadSaveSlot("PreVoid") // restore pre-void state + │ ├─ TransitionToChapter(OriginalChapter) + │ ├─ Spawn player at original death location (or nearest safe spot) + │ │ + │ ├─ [Void-Touched Benefits] + │ │ ├─ GI_HorrorGame.SetSessionFlag("VoidSpaceVisited", true) + │ │ ├─ Player gains passive: +10% damage vs enemies + │ │ ├─ Player can see hidden void objects in real world + │ │ └─ New dialogue option: "I've touched the void" + │ │ + │ ├─ Health/Inventory restored to pre-death state + │ └─ WBP_NotificationToast.Show("You are now Void-Touched") + │ + └─ Achievement: "Touched the Void" unlocked +``` + +--- + +## I_Persistable Implementation + +Every actor that needs to save/restore its state implements `I_Persistable`: + +### Persistent Actor Types + +| Actor | Saved Data | Restored On | +|-------|-----------|------------| +| `BP_Door_*` | bIsOpen, bIsLocked, bIsBarricaded, DoorRotation | Level load, death respawn | +| `BP_Pickup_*` | bIsCollected (if true → destroy on load) | Level load, death respawn | +| `BP_Enemy_*` | bIsDead, CorpseLocation, CorpseRotation | Level load, death respawn | +| `BP_Checkpoint_*` | bActivated, ActivationCount | Level load | +| `BP_PuzzleDeviceActor_*` | bIsSolved, CurrentStep, PuzzleStateData | Level load, death respawn | +| `BP_ContainerInventory` (chests/desks) | IsOpen, LootedItems, ContainerState | Level load, death respawn | +| `BP_NPC_Survivor` | Location, bIsFollowing, bIsAlive | Level load | + +### I_Persistable Interface Functions + +``` +SaveState() → FPersistentData + ├─ Actor serializes its current state into a struct + └─ Returns FPersistentData (includes ActorClass, ActorName, StatePayload) + +LoadState(FPersistentData) + ├─ Actor restores its state from saved data + └─ Returns Boolean (success/fail) + +GetPersistenceID() → FName + └─ Returns unique ID for this actor instance (level name + actor name) +``` + +--- + +## Run History Tracker + +### BPC_RunHistoryTracker Data + +``` +Current Run Stats: + ├─ TotalDeaths (Int32) + ├─ DeathLocations (Array) + │ └─ { Location, Cause, Chapter, Timestamp } + ├─ CheckpointsActivated (Array) + ├─ TimePerChapter (Map) + ├─ ItemsFound (Int32 / TotalItems) + ├─ EnemiesKilled (Int32) + ├─ ScaresExperienced (Int32) + └─ PlaystyleTag (GameplayTag) + +Displayed on: + ├─ WBP_DeathScreen → Deaths + Chapter + Time + ├─ WBP_GamePauseMenu → Chapter + Deaths + Time + └─ WBP_DeathScreen (post-credits) → Full summary +``` + +--- + +## Save/Load UI Flow + +### Save Game Menu + +``` +SS_UIManager.PushMenu(SaveLoadMenu) + │ + ├─ [Display Save Slots] + │ └─ For each slot in SS_SaveManager.GetAllSlots(): + │ ├─ Show slot thumbnail, chapter, playtime, timestamp + │ ├─ Empty slot → "Empty — [Create New Save]" + │ └─ Occupied slot → [Load] [Delete (hold to confirm)] + │ + ├─ [Load Game] + │ ├─ Selected slot → SS_SaveManager.LoadSaveSlot(SlotIndex) + │ ├─ Get SaveHeader.ChapterTag + │ ├─ TransitionToChapter(ChapterTag) + │ └─ On level loaded: BPC_PlayerRespawnSystem.RestoreState() + │ + ├─ [Delete Save] + │ ├─ Hold button for 1.5s to confirm (prevents accidental deletion) + │ ├─ SS_SaveManager.DeleteSaveSlot(SlotIndex) + │ └─ Refresh slot list + │ + └─ [New Save — Manual] + └─ SS_SaveManager.CreateManualSave(SlotIndex, "Manual") +``` + +--- + +## Blueprint Wiring Checklist + +### Save System +- [ ] Configure `SS_SaveManager` with 3 default save slots +- [ ] Implement `I_Persistable` on all 7 persistent actor types +- [ ] Wire `BPC_PersistentWorldStateRecorder` to capture world state at checkpoints/death +- [ ] Wire save/load UI through `WBP_GamePauseMenu` and `WBP_GameMainMenu` + +### Checkpoints +- [ ] Create `BP_Checkpoint_SafeRoom`, `BP_Checkpoint_Mirror`, `BP_Checkpoint_BasementGate`, `BP_Checkpoint_VoidThreshold` +- [ ] Wire overlap → auto-save + health/stamina restore + notification + +### Death +- [ ] Wire `BPC_DeathHandlingSystem` → death animation → death screen → respawn +- [ ] Wire `BPC_PersistentCorpseSystem` → leave corpse at death location +- [ ] Wire `BPC_PlayerRespawnSystem` → load checkpoint + restore state +- [ ] Wire `WBP_DeathScreen` with Continue/MainMenu/Void options + +### Void Space +- [ ] Wire `BPC_AltDeathSpaceSystem` → enter void on Shade death +- [ ] Create void puzzle (3 memory shrines) in `L_Asylum_VoidSpace` +- [ ] Wire exit → restore pre-void state + void-touched benefits + +### Run History +- [ ] Wire `BPC_RunHistoryTracker` to log deaths, checkpoints, chapter times +- [ ] Display stats on death screen + post-credits summary + +--- + +## Notes for Expansion + +- Add **cloud saves** (platform-specific: Steam Cloud, PSN, Xbox Live) +- Add **save file encryption** to prevent tampering (anti-cheat) +- Add **ironman/permadeath mode**: disable all saves except checkpoint auto-save, one life only +- Add **save transfer**: export/import saves for cross-platform +- Add **death replay**: camera pans to show how you died (like a replay of last 5 seconds) +- Void Space could have **multiple exits** that lead to different real-world locations +- Multiplayer: save states must be server-authoritative, replicated to joining clients + +--- + +*Save System & Checkpoints for Project Void. See [GAMEINDEX.md](GAMEINDEX.md) for full game structure. See [35_SS_SaveManager.md](../blueprints/05-saveload/35_SS_SaveManager.md) for save subsystem spec.* diff --git a/docs/game/scene-flow.md b/docs/game/scene-flow.md new file mode 100644 index 0000000..24ed497 --- /dev/null +++ b/docs/game/scene-flow.md @@ -0,0 +1,558 @@ +# Scene Flow — Complete Level Loading Architecture + +**Game:** Project Void | **Build Phase:** 2 +**Framework Systems:** 04_GI_GameFramework, 05_GM_CoreGameMode, 44_SS_UIManager, 110_BPC_LoadingScreen, 114_WBP_SplashScreen, 128_SS_EnhancedInputManager + +--- + +## Purpose + +This document defines every scene transition in the game — from engine boot to credits roll. It specifies what loads, what plays, what the player sees, and how control flows between levels. Use this as the definitive reference when wiring level transitions and testing scene flow. + +--- + +## Complete Scene Map + +``` + ┌──────────────────────────────────────┐ + │ GAME BOOT │ + │ GI_HorrorGame.Init bootstraps │ + │ all SS_ subsystems, validates tags │ + └──────────────┬───────────────────────┘ + │ + ▼ + ┌──────────────────────────────────────┐ + │ SCENE 1: L_SplashScreen │ + │ Duration: ~8 seconds (skippable) │ + │ │ Studio Logo (2s, fade) │ + │ │ UE5 Logo (1.5s, fade) │ + │ │ "Project Void" Title (3s, fade) │ + │ │ Health Warning (1.5s, fade) │ + │ │ Audio: MS_MusicBus ambient_loop │ + │ └─ Auto-advance OR Skip → │ + └──────────────┬───────────────────────┘ + │ + ▼ + ┌──────────────────────────────────────┐ + │ SCENE 2: L_MainMenu │ + │ │ 3D Background: Asylum hallway │ + │ │ WBP_GameMainMenu displayed │ + │ │ ┌─────────────────────────┐ │ + │ │ │ NEW GAME │──────┤ + │ │ │ CONTINUE (if save exists)│ │ + │ │ │ LOAD GAME │ │ + │ │ │ SETTINGS │ │ + │ │ │ CREDITS │ │ + │ │ │ QUIT │ │ + │ │ └─────────────────────────┘ │ + │ └────────────────────────────────────┘ + │ + ├─ NEW GAME ─────────────────────────────┐ + │ │ + ├─ CONTINUE ──────────────────────────┐ │ + │ │ │ + └─ SETTINGS ──────────────────┐ │ │ + │ │ │ + ▼ ▼ ▼ + ┌──────────────────────────────────────┐ + │ LOADING SCREEN (transitional) │ + │ │ WBP_LoadingHorror displayed │ + │ │ Progress bar + atmosphere tip │ + │ │ SS_SaveManager.ResetGameState() │ + │ │ (if New Game — fresh state) │ + │ │ SS_SaveManager.LoadSlot() │ + │ │ (if Continue/Load — restore) │ + │ │ GI_GameFramework.SetPhase │ + │ │ (Loading) │ + │ │ Audio: Crossfade to target music │ + │ └─ On level loaded → SetPhase(InGame)│ + └──────────────┬───────────────────────┘ + │ + ▼ + ┌──────────────────────────────────────┐ + │ SCENE 3: L_Asylum_Entry (Tutorial) │ + │ │ Player wakes in padded cell │ + │ │ Opening cutscene: 3-5 seconds │ + │ │ (Camera pan, blur, voiceover) │ + │ │ │ + │ │ Tutorial Sequence: │ + │ │ 1. Move/Look prompt │ + │ │ 2. Interact: Pick up "Note" │ + │ │ 3. Crouch: Navigate vent │ + │ │ 4. Hide: First patient sighting │ + │ │ 5. Objective: "Find a way out" │ + │ │ │ + │ │ First Checkpoint: Safe Room │ + │ │ First Item: Flashlight (on desk) │ + │ │ First Door: Unlocked (tutorial) │ + │ │ │ + │ │ Atmosphere: Light tension │ + │ │ Audio: Ward ambience, creaks │ + │ │ Difficulty: Tutorial (easy) │ + │ │ │ + │ └─ Exit door → TransitionToChapter │ + └──────────────┬───────────────────────┘ + │ + ▼ + ┌──────────────────────────────────────┐ + │ SCENE 4: L_Asylum_WardA │ + │ │ Main Ward Area │ + │ │ │ + │ │ Objectives: │ + │ │ • Find flashlight (if missed) │ + │ │ • Find pistol (security office) │ + │ │ • Restore power (fusebox puzzle) │ + │ │ • Find Keycard Alpha (nurses st.) │ + │ │ │ + │ │ Enemies: │ + │ │ • 2-3 Patient enemies (patrol) │ + │ │ • Optional: Shade teaser (window) │ + │ │ │ + │ │ Puzzles: │ + │ │ • BP_Puzzle_Fusebox │ + │ │ └─ Restore 3 circuit breakers │ + │ │ └─ Opens locked treatment room │ + │ │ │ + │ │ Hiding Spots: │ + │ │ • Lockers (3), Under Bed (2) │ + │ │ │ + │ │ Collectibles: │ + │ │ • 2 Patient Files, 1 Old Photo │ + │ │ │ + │ │ Checkpoints: │ + │ │ • Safe Room (start of area) │ + │ │ • Nurse Station Mirror │ + │ │ │ + │ │ Atmosphere: Tension building │ + │ │ Audio: WardA acoustic preset │ + │ │ Scares: 2 (locker bang, whisper) │ + │ │ │ + │ └─ Alpha Door → WardB │ + └──────────────┬───────────────────────┘ + │ + ▼ + ┌──────────────────────────────────────┐ + │ SCENE 5: L_Asylum_WardB │ + │ │ High-Security Ward │ + │ │ │ + │ │ Objectives: │ + │ │ • Find shotgun (morgue) │ + │ │ • Solve pipe organ puzzle │ + │ │ • Retrieve patient records │ + │ │ • Find Keycard Beta │ + │ │ │ + │ │ Enemies: │ + │ │ • 2-3 Orderly enemies (fast) │ + │ │ • 1-2 Patient enemies │ + │ │ • First Shade encounter (script) │ + │ │ │ + │ │ First Void Shift: │ + │ │ • Trigger: Enter morgue │ + │ │ • Effect: Walls bleed, corridor │ + │ │ loops, whispers intensify │ + │ │ • Duration: 30 seconds │ + │ │ • Exit: Find mirror and interact │ + │ │ │ + │ │ Puzzles: │ + │ │ • BP_Puzzle_PipeOrgan │ + │ │ └─ Play 4-note melody sequence │ + │ │ └─ Opens hidden chapel room │ + │ │ │ + │ │ Collectibles: │ + │ │ • 3 Patient Files, 1 Void Shard │ + │ │ │ + │ │ Atmosphere: Horror ramping │ + │ │ Audio: WardB preset (darker, echo) │ + │ │ Scares: 3 (mirror face, window │ + │ │ slam, breathing behind player) │ + │ │ │ + │ └─ Beta Door → Basement │ + └──────────────┬───────────────────────┘ + │ + ▼ + ┌──────────────────────────────────────┐ + │ SCENE 6: L_Asylum_Basement │ + │ │ Dark Horror Zone │ + │ │ │ + │ │ Special Rule: NO ambient light │ + │ │ • Player MUST use flashlight │ + │ │ • Battery management critical │ + │ │ • Flashlight attracts enemies │ + │ │ │ + │ │ Objectives: │ + │ │ • Find Warden's Key (morgue #4) │ + │ │ • Solve morgue drawer puzzle │ + │ │ • Survive Shade pursuit │ + │ │ • Escape basement via elevator │ + │ │ │ + │ │ Enemies: │ + │ │ • Shade (persistent, teleporting) │ + │ │ • 2-3 Patient enemies (lost) │ + │ │ │ + │ │ Stress System Peaks: │ + │ │ • Constant darkness = +stress │ + │ │ • Shade proximity = +stress │ + │ │ • At Critical: hallucinations │ + │ │ └─ False enemies, voices, │ + │ │ environment warps │ + │ │ │ + │ │ Atmosphere: Maximum horror │ + │ │ Audio: Basement preset (reverb, │ + │ │ dripping water, distant screams) │ + │ │ Scares: 4+ (multiple jump scares) │ + │ │ │ + │ └─ Elevator → Warden's Office │ + └──────────────┬───────────────────────┘ + │ + ▼ + ┌──────────────────────────────────────┐ + │ SCENE 7: L_Asylum_WardensOffice │ + │ │ Climax Area │ + │ │ │ + │ │ Objectives: │ + │ │ • Read Warden's journal │ + │ │ └─ Reveals truth: asylum is │ + │ │ a psychic prison for an │ + │ │ eldritch entity │ + │ │ • Confront the truth │ + │ │ └─ Dialogue choice with entity │ + │ │ └─ Accept / Resist / Sacrifice │ + │ │ │ + │ │ Ending Accumulator evaluates: │ + │ │ • VoidShards collected: 0 1 2 3 │ + │ │ • Survivor NPC saved? Y/N │ + │ │ • Sanity at end: Low/Med/High │ + │ │ • Dialogue choice made │ + │ │ → Determines 3 possible endings │ + │ │ │ + │ │ Atmosphere: Resolution/Tension │ + │ │ Audio: Final confrontation theme │ + │ └─ Ending trigger → L_Ending_Truth │ + └──────────────┬───────────────────────┘ + │ + ┌─────────────┼─────────────┐ + │ │ │ + ▼ ▼ ▼ + ┌────────────┐ ┌────────────┐ ┌────────────┐ + │ ENDING A │ │ ENDING B │ │ ENDING C │ + │ "Escape" │ │ "Merge" │ │ "Sacrifice"│ + │ │ │ │ │ │ + │ You resist │ │ You accept │ │ You sacri- │ + │ the entity │ │ the entity │ │ fice your- │ + │ and flee │ │ and become │ │ self to │ + │ the asylum │ │ its vessel │ │ seal the │ + │ │ │ │ │ entity │ + │ Cutscene: │ │ Cutscene: │ │ Cutscene: │ + │ Running │ │ The asylum │ │ You walk │ + │ through │ │ calms. The │ │ into the │ + │ collapsing │ │ entity │ │ void. Light │ + │ halls. │ │ speaks │ │ fades. │ + │ Daylight. │ │ through you.│ │ Credits. │ + └─────┬──────┘ └─────┬──────┘ └─────┬──────┘ + │ │ │ + └──────────────┼──────────────┘ + │ + ▼ + ┌──────────────────────────────────────┐ + │ SCENE 8: L_Ending_Truth │ + │ │ Closing cinematic plays │ + │ │ Post-credit scene (5s) │ + │ │ │ + │ │ Achievement Unlocks: │ + │ │ • "Escaped the Void" (any ending) │ + │ │ • Ending-specific achievement │ + │ │ • "Collector" (all VoidShards) │ + │ │ • "Pacifist" (killed 0 enemies) │ + │ └─ Auto-advance → L_Credits │ + └──────────────┬───────────────────────┘ + │ + ▼ + ┌──────────────────────────────────────┐ + │ SCENE 9: L_Credits │ + │ │ Scroll credits (30-45 seconds) │ + │ │ Post-credit: WBP_DeathScreen │ + │ │ shows stats: │ + │ │ • Total deaths, time, ending │ + │ │ • Collectibles found │ + │ │ • Ending classification │ + │ │ │ + │ │ Button: "Return to Main Menu" │ + │ │ Button: "New Game+" (if unlocked) │ + │ └─ Return → L_MainMenu │ + └──────────────────────────────────────┘ +``` + +--- + +## Sub-Scenes: Pause, Death, Void Space + +These can trigger from **any gameplay scene** (Scenes 3-7): + +### Pause Branch + +``` +PRESS ESC (IA_PauseMenu) — any gameplay scene + │ + ├─ PC_HorrorController → OpenPauseMenu + │ + ├─ SS_UIManager.PushMenu(PauseMenu) + │ ├─ Game Paused (SetPhase = Paused) + │ ├─ SS_EnhancedInputManager.SetInputModeUIOnly() + │ ├─ Mouse cursor shown + │ ├─ WBP_HUDController hides diegetic HUD + │ └─ WBP_GamePauseMenu displayed + │ + ├─ Pause Menu Options: + │ ├─ RESUME → PopMenu → SetPhase(InGame) → restore + │ ├─ SAVE → SS_SaveManager.CreateManualSave + │ ├─ LOAD → Push LoadGameMenu → select slot → load + │ ├─ SETTINGS → PushMenu(SettingsMenu) + │ ├─ RETURN TO MENU → Confirm → QuitToMenu + │ └─ QUIT TO DESKTOP → Confirm → GI_HorrorGame.QuitToDesktop + │ + └─ On Resume: + ├─ SS_UIManager.PopMenu + ├─ GI_GameFramework.SetGamePhase(InGame) + ├─ SS_EnhancedInputManager.SetInputModeGameOnly() + ├─ Mouse cursor hidden + └─ WBP_HUDController shows diegetic HUD +``` + +### Death Branch (Normal) + +``` +PLAYER HEALTH = 0 — any gameplay scene + │ + ├─ BPC_DeathHandlingSystem triggers + │ ├─ Death animation plays (GASP death montage) + │ ├─ BPC_StateManager.ForcePushState(Death) + │ ├─ WBP_ScreenEffectController → Death fade-out + │ └─ Audio: Death sting → silence + │ + ├─ GM_HorrorGameMode.HandlePlayerDead + │ │ + │ ├─ Check Void Space conditions (see below) + │ │ └─ NOT void-qualified → Normal Respawn + │ │ + │ └─ [Respawn Flow] + │ ├─ Increment PS_HorrorPlayerState.DeathsThisChapter + │ ├─ BPC_PersistentCorpseSystem.LeaveCorpse() + │ ├─ BPC_RunHistoryTracker.LogDeath() + │ ├─ BPC_PersistentWorldStateRecorder.SaveWorldState() + │ ├─ SS_SaveManager.CreateAutoSave("DeathRespawn") + │ │ + │ ├─ [If ALLOWED_DEATHS exceeded → Game Over option] + │ │ ├─ Prompt: "Return to Main Menu?" + │ │ └─ Yes → L_MainMenu, No → respawn anyway + │ │ + │ └─ TransitionToChapter(LastCheckpointChapter) + │ ├─ Loading screen + │ └─ On loaded: BPC_PlayerRespawnSystem.RestoreState() + │ ├─ Health restored (checkpoint value) + │ ├─ Inventory restored (minus lost items) + │ ├─ World state restored (doors, items, enemies) + │ └─ WBP_ScreenEffectController → respawn fade-in +``` + +### Void Space Branch (Special Death) + +``` +TRIGGER: Death cause = "Void" AND first void death + OR: Walk into void portal in Basement + │ + ├─ GM_HorrorGameMode sets bVoidSpaceActive = true + ├─ BPC_AltDeathSpaceSystem.EnterVoidSpace() + │ ├─ TransitionToChapter(Chapter.VoidSpace) + │ └─ Load L_Asylum_VoidSpace + │ + └─ [Void Space Level] + │ + ├─ Environment: + │ ├─ Inverted lighting (black fog, white shadows) + │ ├─ Geometry: Floating, non-Euclidean rooms + │ ├─ Audio: Reversed whispers, heartbeat bass + │ └─ No map/HUD — disoriented + │ + ├─ Objective: "Find the light. Escape the void." + │ ├─ Navigate shifting corridors + │ ├─ Solve void puzzle (3 memory shrines) + │ └─ Reach exit portal → return to real asylum + │ + ├─ On Exit: + │ ├─ BPC_AltDeathSpaceSystem.ExitVoidSpace() + │ ├─ TransitionToChapter(ReturnChapter) + │ │ └─ Return to where player died (or nearby) + │ ├─ GI_HorrorGame.SetSessionFlag("VoidSpaceVisited", true) + │ ├─ Player gains "Void Touched" status: + │ │ └─ Can now see hidden void objects in real world + │ └─ Health/Inventory restored to pre-death state + │ + └─ Achievement: "Touched the Void" +``` + +--- + +## Loading Screen Specification + +Used between every scene transition. Implemented by `BPC_LoadingScreen` (110) + `WBP_LoadingHorror`. + +### WBP_LoadingHorror Widget + +| Element | Type | Content | +|---------|------|---------| +| `Background` | Image | Blurred asylum corridor screenshot | +| `ProgressBar` | Progress Bar | 0-100% level load progress | +| `TipText` | Text Block | Rotating atmosphere/lore tips | +| `TitleText` | Text Block | Chapter name (e.g., "Ward A — Patient Wing") | +| `LoadingSpinner` | Image (animated) | Small rotating void symbol | + +### Tip Rotation (8-10 second cycle) + +Shows random tip from a `DT_LoadingTips` Data Table: + +| Tip | Category | +|-----|----------| +| "Darkness is your ally. Enemies can't see what they can't hear." | Stealth | +| "Batteries are scarce. Use the flashlight sparingly." | Resource | +| "Listen. The asylum speaks to those who pay attention." | Atmosphere | +| "Not all doors need a key. Some need a different kind of opening." | Puzzle | +| "The void watches. It remembers. It waits." | Lore | +| "Running attracts attention. Sometimes walking is survival." | Stealth | +| "Check every drawer. The asylum hides its secrets well." | Exploration | +| "Your sanity is as important as your health." | Survival | +| "The patients aren't your enemy. They're victims too." | Lore | +| "Press ESC to pause. The horror will wait." | Meta | + +--- + +## Main Menu Level Design + +### L_MainMenu.umap + +``` +Level Layout (simple): + ┌────────────────────────────────────────────┐ + │ │ + │ [3D Background: Asylum Hallway] │ + │ - Static camera slowly pans left/right │ + │ - Dim lighting, atmospheric fog │ + │ - Distant flickering light bulb │ + │ - Occasional distant sound (creak) │ + │ │ + │ ┌─────────────────────┐ │ + │ │ PROJECT VOID │ │ + │ │ ═══════════════ │ │ + │ │ │ │ + │ │ [ NEW GAME ] │ │ + │ │ [ CONTINUE ] │ │ + │ │ [ LOAD GAME ] │ │ + │ │ [ SETTINGS ] │ │ + │ │ [ CREDITS ] │ │ + │ │ [ QUIT ] │ │ + │ │ │ │ + │ │ v1.0 — UE5 Demo │ │ + │ └─────────────────────┘ │ + │ │ + └────────────────────────────────────────────┘ + +Setup in UE5: + 1. Create new Level: L_MainMenu + 2. Add a BoxReflectionCapture + SkyLight (low intensity) + 3. Add a PostProcessVolume with horror color grading: + - Saturation: 0.7 + - Contrast: 1.1 + - Color Grading: cool blue tint + 4. Place a CameraActor for the background + - Animate via Matinee or Sequencer (slow pan) + 5. Add BP_RoomAudioZone with Lobby preset + 6. In Level Blueprint: OnBeginPlay → SS_UIManager.ShowMenu(MainMenu) + 7. In World Settings: GameMode Override = GM_HorrorGameMode +``` + +--- + +## Scene Transition Blueprint Pattern + +Every chapter transition follows this pattern. Use this as a macro: + +``` +TransitionToChapter (ChapterTag: GameplayTag) + │ + ├─ 1. PRE-TRANSITION + │ ├─ GI_GameFramework.SetGamePhase(Loading) + │ ├─ SS_UIManager.HideAllMenus() + │ ├─ BPC_LoadingScreen.Show(WBP_LoadingHorror) + │ └─ BPC_StateManager.ForcePushState(Loading) + │ + ├─ 2. WORLD STATE CAPTURE + │ ├─ BPC_PersistentWorldStateRecorder.SaveWorldState() + │ └─ SS_SaveManager.CreateAutoSave("ChapterTransition") + │ + ├─ 3. LEVEL LOAD + │ ├─ GM_HorrorGameMode.HorrorLevelNames.Find(ChapterTag) + │ └─ OpenLevel(LevelName) + │ + ├─ 4. POST-LOAD (Level Blueprint OnBeginPlay) + │ ├─ Set ChapterTag on GS_HorrorGameState + │ ├─ BPC_NarrativeStateSystem.OnChapterStart(ChapterTag) + │ ├─ BPC_AtmosphereStateController.LoadProfile(ChapterTag) + │ ├─ BP_RoomAudioZone auto-activates on overlap + │ ├─ BPC_PacingDirector.SetIntensityBand(ChapterTag) + │ └─ Spawn player at level's PlayerStart actor + │ + └─ 5. RESUME + ├─ GI_GameFramework.SetGamePhase(InGame) + ├─ BPC_LoadingScreen.Hide() + └─ BPC_StateManager.RestorePreviousState() +``` + +--- + +## Blueprint Wiring Checklist + +### L_SplashScreen Level +- [ ] Create empty level, add DirectionalLight + SkyLight +- [ ] Create WBP_SplashHorror widget (child of WBP_SplashScreen) +- [ ] In Level Blueprint: OnBeginPlay → AddToViewport(WBP_SplashHorror) +- [ ] Wire splash sequence: StudioLogo → EngineLogo → TitleCard → Warning → auto-advance +- [ ] On sequence complete: OpenLevel("L_MainMenu") +- [ ] Bind Skip: Any key press → jump to end of sequence + +### L_MainMenu Level +- [ ] Create level with 3D background + CameraActor +- [ ] Create WBP_GameMainMenu widget (child of WBP_MainMenu) +- [ ] In Level Blueprint: OnBeginPlay → SS_UIManager.ShowMenu(MainMenu) +- [ ] Wire New Game → LoadingScreen → L_Asylum_Entry +- [ ] Wire Continue → SS_SaveManager.LoadLatest → LoadingScreen → target level +- [ ] Wire Settings → SS_UIManager.PushMenu(SettingsMenu) +- [ ] Wire Credits → L_Credits +- [ ] Wire Quit → Confirm dialog → GI_HorrorGame.QuitToDesktop + +### Loading Screen +- [ ] Create WBP_LoadingHorror (child of BPC_LoadingScreen's widget class) +- [ ] Add progress bar, tip text, chapter title +- [ ] Create DT_LoadingTips Data Table with 10 tips +- [ ] Wire: OnShow → start tip rotation timer (every 8s, random tip) +- [ ] Wire: OnProgress → update progress bar +- [ ] Wire: OnHide → stop timer, remove from viewport + +### Death Screen (overlay, not separate level) +- [ ] Create WBP_DeathScreen widget +- [ ] Show on death with: "You Died" + stats (chapter, deaths, time) +- [ ] Button: "Continue" → trigger respawn flow +- [ ] Button: "Return to Menu" → L_MainMenu + +--- + +## Notes for Expansion + +- Each chapter could have unique **loading screen backgrounds** (screenshots of key locations) +- Add **seamless streaming** for smaller areas — use `LoadStreamLevel` instead of `OpenLevel` for connected zones +- The void space could be **procedurally generated** — use `BPC_AdaptiveEnvironmentDirector` to generate rooms +- Consider adding a **chapter select** screen for debug/testing (bound to dev cheats) +- The tutorial sequence could have **difficulty detection** — if player dies once, tone down enemies +- Add a **"Return to Checkpoint"** option alongside "Continue" on the death screen + +--- + +*Scene Flow for Project Void. See [GAMEINDEX.md](GAMEINDEX.md) for full game structure. See [44_SS_UIManager.md](../blueprints/06-ui/44_SS_UIManager.md) for menu stack management.* diff --git a/docs/game/state-gating-examples.md b/docs/game/state-gating-examples.md new file mode 100644 index 0000000..975f9f8 --- /dev/null +++ b/docs/game/state-gating-examples.md @@ -0,0 +1,287 @@ +# State Gating Examples — Game-Specific State Rules + +**Game:** Project Void | **Build Phase:** 14 (Data Asset configuration) +**Framework Systems:** 130_BPC_StateManager, 131_DA_StateGatingTable + +--- + +## Purpose + +Defines game-specific state gating rules added to `DA_StateGatingTable` (131). These rules determine what actions are permitted in each game state. Designers can modify these without touching any Blueprint code. + +--- + +## How State Gating Works (Quick Refresher) + +``` +Any system that wants to perform an action: + + Actor → BPC_StateManager.IsActionPermitted(ActionTag) + │ + ├─ StateManager checks DA_StateGatingTable for rule matching ActionTag + ├─ Rule specifies which states BLOCK this action + ├─ If current state matches blocked state: + │ └─ Return { bPermitted = false, BlockReason = "Cannot [Action] — [Reason]" } + └─ If current state doesn't match: + └─ Return { bPermitted = true } + +Result struct: S_ActionPermissionResult + ├─ bPermitted: Boolean + ├─ BlockReason: FText (shown to player via InteractionPromptDisplay) + └─ BlockingState: GameplayTag (for debug/logging) +``` + +--- + +## Framework Default Rules (from DA_StateGatingTable, 37 rules) + +These come from the framework and apply to **all** games: + +| Action | Blocked By | +|--------|-----------| +| `Move` | Death, Cutscene, Loading, Exhausted | +| `Look` | Cutscene, Loading | +| `Interact` | Death, Cutscene, Loading, Hiding, Reloading | +| `Fire` | Death, Cutscene, Loading, Hiding, Reloading, Exhausted, Sprinting | +| `Reload` | Death, Cutscene, Loading, Hiding, Exhausted | +| `Sprint` | Death, Cutscene, Loading, Crouching, Exhausted | +| `Crouch` | Death, Cutscene, Loading, Sprinting | +| `Jump` | Death, Cutscene, Loading, Crouching | +| `Equip` | Death, Cutscene, Loading, Hiding, Reloading | +| `UseItem` | Death, Cutscene, Loading, Hiding | +| `Hide` | Death, Cutscene, Loading, Sprinting, Exhausted, InCombat | +| `Pause` | Death, Cutscene, Loading, AltDeathSpace | +| ... | (30+ more rules — see 131_DA_StateGatingTable.md) | + +--- + +## Game-Specific Rules (Project Void) + +These are added on top of the framework defaults. They represent horror-game-specific restrictions. + +### 1. Combat Rules + +| Action Tag | Blocked By | Block Reason | Notes | +|-----------|-----------|-------------|-------| +| `Fire` | `State.InVoidSpace` | "Your weapon has no power here." | Weapons don't work in void space | +| `Fire` | `State.Catatonic` | "Your hands shake too much to aim." | Stress 100 = can't shoot | +| `Reload` | `State.InVoidSpace` | "Ammunition is meaningless in the void." | Void space blocks reload | +| `Melee` | `State.InVoidSpace` | "You swing at shadows." | Melee also useless in void | + +### 2. Void Space Rules + +| Action Tag | Blocked By | Block Reason | Notes | +|-----------|-----------|-------------|-------| +| `Interact` | `State.VoidShiftActive` | "Reality is unstable. Nothing responds correctly." | During void shift events, interaction is unreliable (50% chance success) | +| `Move` | `State.VoidParalyzed` | "The void holds you still." | Scripted moments where player is frozen | +| `Pause` | `State.AltDeathSpace` | "There is no escape from the void." | No pause menu in void space | +| `OpenInventory` | `State.AltDeathSpace` | "Your belongings feel distant here." | No inventory in void space | +| `UseItem` | `State.AltDeathSpace` | "Items from your world have no effect." | Can't use consumables in void space | + +### 3. Horror-Specific Rules + +| Action Tag | Blocked By | Block Reason | Notes | +|-----------|-----------|-------------|-------| +| `Sprint` | `State.Exhausted` | "You're too exhausted to run." | After stamina depleted for 3+ seconds | +| `Sprint` | `State.Catatonic` | "Your body won't obey." | Stress 100 = no sprint | +| `Interact` | `State.InCombat` | "Too dangerous! Find cover first." | Can't search drawers while enemy is attacking | +| `Hide` | `State.InCombat` | "Hide quickly before they see you!" | Transition period of 2s after combat starts, then allowed | +| `Hide` | `State.Catatonic` | "You can't think clearly enough to hide." | Stress 100 = can't hide | +| `Flashlight` | `State.Exhausted` | "Your arm is too weak to hold the light." | Exhausted state dims flashlight to 25% instead of blocking | +| `OpenWatch` | `State.Hiding` | — | **ALLOWED** (override framework block) — can check inventory while hiding | + +### 4. Narrative/Dialogue Rules + +| Action Tag | Blocked By | Block Reason | Notes | +|-----------|-----------|-------------|-------| +| `All` (wildcard) | `State.InDialogueChoice` | "Make your choice." | ALL actions blocked during dialogue choice | +| `Move` | `State.InDialogueChoice` | "You stand before the entity, frozen." | Can't walk away mid-choice | +| `Equip` | `State.InCutscene` | — | Framework default already blocks | +| `Pause` | `State.InEndingSequence` | "This moment cannot be paused." | Ending cutscene blocks pause | + +### 5. Death State Rules + +| Action Tag | Blocked By | Block Reason | Notes | +|-----------|-----------|-------------|-------| +| `All` (wildcard) | `State.Dead` | "You are dead." | ALL actions blocked | +| `Pause` | `State.Dead` | "Press Continue to respawn." | Pause menu blocked on death screen (death screen is the menu) | +| `Look` | `State.Dead` | — | **ALLOWED** (override) — camera still follows ragdoll briefly | + +--- + +## DA_StateGatingTable Game-Specific Configuration + +### How to Add These Rules in UE5 + +1. Open `DA_StateGatingTable` (Content/Framework/State/) +2. **Duplicate** it → `DA_StateGatingTable_Horror` (Content/Game/DataAssets/) +3. The framework `DA_StateGatingTable` has 37 base rules. The game-specific table **inherits** them. +4. Add new entries for game-specific rules. + +**Rule: NEVER modify `DA_StateGatingTable` in Framework/. Use your game copy.** + +### Rule Entry Structure + +``` +S_StateGatingRule (struct): + ├─ ActionTag: GameplayTag (e.g., Framework.Action.Fire) + ├─ BlockedStateTags: Array (e.g., State.InVoidSpace) + ├─ BlockReason: FText (shown to player) + ├─ Priority: Int32 (higher = overrides lower priority rules) + └─ bOverrideFramework: Boolean (true = replaces framework rule for this action) +``` + +### Example Entry: Block Fire in Void Space + +``` +ActionTag: Framework.Action.Fire +BlockedStates: [State.InVoidSpace, State.AltDeathSpace] +BlockReason: "Your weapon has no power here." +Priority: 100 +OverrideFramework: false (ADD to framework blocks, not REPLACE) +``` + +### Example Entry: ALLOW Interact During Hiding (Override) + +``` +ActionTag: Framework.Action.OpenWatch +BlockedStates: [] (empty = no blocks = ALWAYS permitted) +BlockReason: "" +Priority: 200 (HIGHER than framework default) +OverrideFramework: true (REPLACE framework rule — framework blocks OpenWatch during Hiding) +``` + +--- + +## Runtime State Management (BPC_StateManager Integration) + +### Force Push States (Scripted Moments) + +``` +States that are force-pushed by game systems: + + BPC_CutsceneBridge → ForcePushState(InCutscene) + └─ Blocks: All actions except SkipCutscene + + BPC_DeathHandlingSystem → ForcePushState(Dead) + └─ Blocks: All actions + + BPC_VoidShiftSystem → ForcePushState(VoidShiftActive) + └─ Blocks: Fire, Reload, Melee, Interact(50%) + + BPC_DialogueChoiceSystem → ForcePushState(InDialogueChoice) + └─ Blocks: All actions except dialogue input + + BPC_StressSystem (at Catatonic) → ForcePushState(Catatonic) + └─ Blocks: Fire, Sprint, Hide +``` + +### Overlay States (Simultaneous) + +``` +States that layer on top of base state: + + BPC_HidingSystem → SetOverlayState(Hiding) + └─ Blocks: Fire, Reload, Sprint, Jump + └─ Allows: Look, Interact (exit hide), Crouch + + BPC_EquipmentSlotSystem → SetOverlayState(Aiming) + └─ Blocks: Sprint, Jump + └─ Allows: Fire (with ADS bonus), Reload, Crouch + + BPC_StaminaSystem → SetOverlayState(Exhausted) + └─ Blocks: Sprint, Jump + └─ Allows: Move (walk only), Interact, Fire +``` + +### Vital Signals (BPC_StateManager tracks) + +``` +BPC_StateManager.UpdateVitalSignals() is called every 0.5s: + + E_PlayerVitalSignals: + ├─ Normal → Health > 50 AND Stress < 50 AND Stamina > 30 + ├─ Wounded → Health < 50 + ├─ Stressed → Stress > 50 + ├─ Exhausted → Stamina < 20 + └─ Critical → Health < 25 OR Stress > 90 + +Systems read vital signals to modify behavior: + ├─ WBP_DiegeticHUDFrame → changes health bar color per signal + ├─ SS_AudioManager → modifies heartbeat audio parameter + ├─ BPC_CameraStateLayer → adjusts FOV and blur per signal + └─ BPC_MemoryDriftSystem → increases hallucination intensity +``` + +--- + +## GASP Liaison (Movement ↔ State) + +### Movement State → Action State Mapping + +``` +BPC_MovementStateSystem reports movement mode to BPC_StateManager: + + MovingMode.Walking → State.Walking + MovingMode.Sprinting → State.Sprinting + MovingMode.Crouching → State.Crouching + MovingMode.Falling → State.Falling (no jump/equip actions) + MovingMode.Swimming → State.Swimming (if water areas added) + MovingMode.Ladder → State.Climbing (no fire/equip actions) + +BPC_StateManager enforces: + ├─ Can't Sprint → if State.Is(Exhausted) OR State.Is(Catatonic) + ├─ Can't Crouch → if State.Is(Sprinting) + └─ Can't Jump → if State.Is(Crouching) OR State.Is(Exhausted) +``` + +--- + +## Debugging State Gating + +### In WBP_DebugMenu → State Viewer tab: + +``` +Display: + ├─ Current Action State: "Walking" + ├─ Current Overlay States: ["Hiding"] + ├─ Vital Signal: "Wounded" + ├─ Force Stack Depth: 2 + │ ├─ [0] InCutscene (pushed by BPC_CutsceneBridge) + │ └─ [1] Dead (pushed by BPC_DeathHandlingSystem) + │ + ├─ Action Test: + │ ├─ [Fire] → BLOCKED: "Cannot Fire — you are in Hiding" + │ ├─ [Interact] → BLOCKED: "Cannot Interact — cutscene playing" + │ └─ [Look] → PERMITTED + │ + └─ Gating Table Source: DA_StateGatingTable_Horror (Game/DataAssets/) +``` + +--- + +## Blueprint Wiring Checklist + +- [ ] Duplicate `DA_StateGatingTable` → `DA_StateGatingTable_Horror` in `Game/DataAssets/` +- [ ] Add all 15+ game-specific gating rules +- [ ] Configure `BP_HorrorPlayerCharacter` → BPC_StateManager → set `GatingTable = DA_StateGatingTable_Horror` +- [ ] Wire all force-push states (cutscene, death, void shift, dialogue choice, catatonic) +- [ ] Wire all overlay states (hiding, aiming, exhausted) +- [ ] Wire GASP liaison: MovementState → BPC_StateManager movement update +- [ ] Test every blocked action shows correct `BlockReason` in HUD +- [ ] Verify `WBP_InteractionPromptDisplay` reads `OnActionDenied` dispatcher + +--- + +## Notes for Expansion + +- New states can be added by creating a new **GameplayTag** in `DT_Tags_State.csv` +- Designers can add gating rules to `DA_StateGatingTable_Horror` without touching any Blueprint +- Priority system allows game rules to override framework rules cleanly +- Consider adding **"semi-permitted"** actions: permitted but with penalty (e.g., flashlight dims when exhausted) +- Add **per-state camera layers**: each state automatically configures FOV, offset, blur +- Multiplayer: state gating is server-authoritative; client requests are validated server-side + +--- + +*State Gating Examples for Project Void. See [GAMEINDEX.md](GAMEINDEX.md) for full game structure. See [bpc-statemanager.md](../architecture/bpc-statemanager.md) for full State Manager architecture. See [130_BPC_StateManager.md](../blueprints/16-state/130_BPC_StateManager.md) for State Manager spec.* diff --git a/docs/game/ui-overrides.md b/docs/game/ui-overrides.md new file mode 100644 index 0000000..92fd4ed --- /dev/null +++ b/docs/game/ui-overrides.md @@ -0,0 +1,704 @@ +# UI Overrides — Game-Specific Widget Blueprints + +**Game:** Project Void | **Build Phase:** 11 +**Framework Systems:** 44_SS_UIManager through 57_WBP_SettingsMenu (all 14 UI systems) +**Demonstrated By:** 8 game-specific WBP_ child widgets + +--- + +## Purpose + +Defines every game-specific Widget Blueprint override. Each is a **child** of the framework WBP_ parent, inheriting all framework logic while adding horror-game visual styling, branding, and game-specific menu options. + +**Rule:** Game widgets are children of framework widgets. They override visual styling and add game-specific buttons/options. Never rebuild framework logic in game widgets. + +--- + +## Widget Index + +| Game Widget | Parent (Framework) | Purpose | +|-------------|-------------------|---------| +| `WBP_GameHUDController` | `WBP_HUDController` (47) | Root HUD — horror-themed diegetic layout | +| `WBP_GameMainMenu` | `WBP_MainMenu` (51) | "Project Void" title screen | +| `WBP_GamePauseMenu` | `WBP_PauseMenu` (55) | In-game pause with horror styling | +| `WBP_GameInventoryMenu` | `WBP_InventoryMenu` (49) | Inventory grid with asylum aesthetic | +| `WBP_SplashHorror` | `WBP_SplashScreen` (114) | Boot splash sequence | +| `WBP_JournalHorror` | `WBP_JournalDocumentViewer` (50) | Document reader with aged-paper styling | +| `WBP_DeathScreen` | `UserWidget` (custom) | Death/void space screen overlay | +| `WBP_LoadingHorror` | — (used by BPC_LoadingScreen) | Level loading with tips | + +--- + +## 1. WBP_GameHUDController — Root HUD + +**Parent:** `WBP_HUDController` | **Asset Path:** `Content/Game/UI/WBP_GameHUDController.uasset` + +### Child Widgets (auto-created by parent) + +| Widget | Position | Visibility | +|--------|----------|------------| +| `WBP_DiegeticHUDFrame` (46) | Bottom-center (wristwatch position) | Gameplay only | +| `WBP_InteractionPromptDisplay` (48) | Center-bottom (crosshair area) | When focused on interactable | +| `WBP_ObjectiveDisplay` (54) | Top-right | When objective active | +| `WBP_NotificationToast` (53) | Top-center (slide-in) | On events | +| `WBP_ScreenEffectController` (56) | Full screen (behind all UI) | Always (transparent until effect) | +| `WBP_AccessibilityUI` (45) | Bottom-center (subtitles) | Dialogue playing | + +### Game-Specific Overrides + +#### Visual Styling (Class Defaults) + +| Property | Framework Default | Horror Override | +|----------|-----------------|-----------------| +| `HUDTintColor` | White | Pale amber `#D4A574` (aged paper) | +| `HUDFontFamily` | Roboto | `Chiller` or serif horror font | +| `CrosshairStyle` | Dot | Faint vertical line (no crosshair — diegetic aim) | +| `HUDBackgroundOpacity` | 0.5 | 0.2 (minimalist) | +| `HealthBarColor` | Red | Deep crimson `#8B0000` | +| `StaminaBarColor` | Yellow | Faded gold `#B8860B` | +| `StressBarColor` | Purple | Void purple `#4B0082` | + +#### Functions to Override + +``` +Override: UpdateDiegeticHUDFrame + │ + ├─ Parent: UpdateDiegeticHUDFrame (calls framework HUD update logic) + │ + └─ [Horror-Specific Additions] + ├─ Get BPC_StateManager → GetHeartRate() + │ └─ WBP_DiegeticHUDFrame → SetHeartRateIndicator(rate) + │ └─ Visual: subtle heartbeat pulse on health bar edge + │ + ├─ Get BPC_StressSystem → GetCurrentStressTier() + │ └─ WBP_ScreenEffectController → UpdateStressVignette(tier) + │ ├─ Calm: no effect + │ ├─ Uneasy: very subtle edge darkening + │ ├─ Disturbed: peripheral blur + vignette + │ ├─ Breaking: tunnel vision + color desaturation + │ └─ Catatonic: near-black edges, breathing effect + │ + └─ Get BPC_HidingSystem → GetBreathHoldRemaining() + └─ WBP_DiegeticHUDFrame → ShowBreathHoldMeter(seconds) + └─ Visual: shrinking circle near crosshair area +``` + +#### Phase Visibility (inherited from parent, overridden) + +| Game Phase | HUD Visible | Notes | +|-----------|:---:|-------| +| `MainMenu` | No | Menu layer active instead | +| `Loading` | No | Loading screen visible | +| `InGame` | Yes | Full HUD | +| `Paused` | No | Dimmed, pause menu overlay | +| `Cutscene` | No | Clean cinematic view | +| `DeathLoop` | Partial | Only death screen overlay | +| `AltDeathSpace` | No | Void has its own UI | +| `Credits` | No | Credits widget active | +| `PostGame` | Partial | Stats overlay | + +--- + +## 2. WBP_GameMainMenu — Title Screen + +**Parent:** `WBP_MainMenu` | **Asset Path:** `Content/Game/UI/WBP_GameMainMenu.uasset` + +### Layout + +``` +┌──────────────────────────────────────────────────────┐ +│ │ +│ │ +│ ██████╗ ██████╗ ██████╗ ██╗ │ +│ ██╔══██╗██╔══██╗██╔═══██╗ ██║ │ +│ ██████╔╝██████╔╝██║ ██║ ██║ │ +│ ██╔═══╝ ██╔══██╗██║ ██║██ ██║ │ +│ ██║ ██║ ██║╚██████╔╝╚█████╔╝ │ +│ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚════╝ │ +│ │ +│ ██╗ ██╗ ██████╗ ██╗██████╗ │ +│ ██║ ██║██╔═══██╗██║██╔══██╗ │ +│ ██║ ██║██║ ██║██║██║ ██║ │ +│ ╚██╗ ██╔╝██║ ██║██║██║ ██║ │ +│ ╚████╔╝ ╚██████╔╝██║██████╔╝ │ +│ ╚═══╝ ╚═════╝ ╚═╝╚═════╝ │ +│ │ +│ [ NEW GAME ] │ +│ [ CONTINUE ] (dim if no save) │ +│ [ LOAD GAME ] │ +│ [ SETTINGS ] │ +│ [ CREDITS ] │ +│ [ QUIT ] │ +│ │ +│ v1.0 — UE5 Demo │ +│ "In the void, memory is currency." │ +│ │ +└──────────────────────────────────────────────────────┘ +``` + +### Overrides from WBP_MainMenu + +| Element | Framework | Horror Override | +|---------|-----------|----------------| +| Title | Plain text | Animated glitch-effect title (Timeline: opacity flicker + position jitter) | +| Background | Solid color | Semi-transparent 3D scene visible behind UI | +| Buttons | Square, solid | Faded rounded, amber highlight on hover | +| Font | System default | Horror serif font | +| Continue button | Standard | Reads "Continue the Nightmare" | +| Quit button | Standard | Reads "Wake Up" (thematic) | +| Version text | Bottom-right | Bottom-center with tagline | +| Ambient effect | None | Occasional text glitch on title (every 8-12 seconds) | + +### Functions + +#### Override: OnConstruct + +``` +OnConstruct + │ + ├─ Parent: OnConstruct (check SS_SaveManager.HasAnySave for Continue) + │ + └─ [Horror Additions] + ├─ StartTitleGlitchTimer (random 8-12 second interval) + │ └─ Title Text → SetRenderOpacity(random 0.7-1.0) + position jitter + │ + ├─ SS_AudioManager.PlayMusic("menu_theme", MS_MusicBus) + │ └─ Fade in from 0.0 to 1.0 over 3 seconds + │ + └─ Get GI_HorrorGame → TotalDeaths + └─ If > 0: show small text "Deaths: [N]" below version + +Override: OnNewGame + │ + ├─ Parent: OnNewGame (calls SS_SaveManager.ResetGameState) + ├─ GI_HorrorGame → SetSessionFlag("NewGameStarted", true) + ├─ GI_HorrorGame → SetGamePhase(Loading) + ├─ OpenLevel("L_Asylum_Entry") + └─ SS_AudioManager.FadeOutMusic(2.0) + +Override: OnContinue + │ + ├─ Parent: OnContinue (load latest save) + ├─ SS_SaveManager.LoadLatestSaveSlot() + ├─ Get SaveHeader → LastChapterTag + └─ GI_HorrorGame → TransitionToChapter(LastChapterTag) + +Override: OnQuit + │ + ├─ Show confirmation: "Are you sure you want to wake up?" + │ ├─ Yes → GI_HorrorGame.QuitToDesktop() + │ └─ No → close confirmation + └─ (Remove standard quit confirm, use themed text) +``` + +--- + +## 3. WBP_GamePauseMenu — Pause Screen + +**Parent:** `WBP_PauseMenu` | **Asset Path:** `Content/Game/UI/WBP_GamePauseMenu.uasset` + +### Layout + +``` +┌────────────────────────────────────────┐ +│ │ +│ ┌──────────────┐ │ +│ │ PAUSED │ │ +│ └──────────────┘ │ +│ │ +│ [ RESUME ] │ +│ [ SAVE GAME ] │ +│ [ LOAD GAME ] │ +│ [ SETTINGS ] │ +│ [ RETURN TO MENU ] │ +│ [ WAKE UP (QUIT) ] │ +│ │ +│ ───── Chapter: Ward A ───── │ +│ Deaths: 2 Time: 00:14:32 │ +│ │ +└────────────────────────────────────────┘ +``` + +### Game-Specific Functions + +``` +Override: OnConstruct + │ + ├─ Parent: OnConstruct + │ + └─ [Show Session Stats] + ├─ GS_HorrorGameState → GetSessionTimeFormatted() + │ └─ Set TimeText + ├─ PS_HorrorPlayerState → DeathsThisChapter + │ └─ Set DeathsText + └─ GM_HorrorGameMode → GetCurrentChapter() + └─ Set ChapterText + +Override: OnResume + │ + ├─ Parent: OnResume (close menu, set phase) + └─ SS_AudioManager.PlaySFX("menu_close") + +Override: OnSave + │ + ├─ SS_SaveManager.CreateManualSave("PauseSave") + ├─ Show toast: "Progress saved." + └─ SS_AudioManager.PlaySFX("save_confirm") + +Override: OnQuitToMenu + │ + ├─ Show confirmation: "Return to main menu? Unsaved progress will be lost." + │ ├─ Yes → SS_SaveManager.QuickSave → OpenLevel("L_MainMenu") + │ └─ No → close confirmation + └─ (Add quick-save before quit for safety) +``` + +--- + +## 4. WBP_GameInventoryMenu — Inventory Screen + +**Parent:** `WBP_InventoryMenu` (49) | **Asset Path:** `Content/Game/UI/WBP_GameInventoryMenu.uasset` + +### Visual Overrides + +| Element | Framework Default | Horror Override | +|---------|-----------------|-----------------| +| Grid Background | Dark gray | Aged parchment texture | +| Slot Border | Thin white | Worn leather frame | +| Item Icon Background | None | Vignette behind each icon | +| Category Tabs | All/Weapons/Items | All/Weapons/Tools/Keys/Documents | +| Weight Bar | Bottom | Bottom with thematic label "Burden" | +| Selected Item Info | Right panel | Right panel with "flavor text" from item desc | +| Font | System | Typewriter/courier for item names | + +### Additional Functions + +``` +Override: OnItemSelected(ItemData, SlotIndex) + │ + ├─ Parent: OnItemSelected (framework: show item details, enable actions) + │ + └─ [Horror Additions] + ├─ If ItemData.ItemType == Document: + │ └─ Show "Read" button → push WBP_JournalHorror with document + │ + ├─ If ItemData.bIsKeyItem: + │ └─ Show "KEY ITEM" badge on item detail panel + │ └─ Warning text: "Cannot be discarded" + │ + └─ Show item flavor text below description + └─ Small, italic: "It feels cold to the touch." + +Override: UpdateGrid + │ + ├─ Parent: UpdateGrid + │ + └─ [Horror Additions] + ├─ Get PS_HorrorPlayerState → GetSanityPercentage() + │ └─ If < 25% (Breaking): items shift slightly on grid refresh + │ └─ Subtle: slot positions jitter 1-2px randomly + │ (Memory Drift System bleed into UI) + │ + └─ BPC_CollectibleTracker → GetFoundCount() + GetTotalCount() + └─ Display at bottom: "Memories Recovered: [N]/15" +``` + +--- + +## 5. WBP_SplashHorror — Boot Splash Screen + +**Parent:** `WBP_SplashScreen` (114) | **Asset Path:** `Content/Game/UI/WBP_SplashHorror.uasset` + +### Splash Sequence + +``` +Sequence (inherits parent's FSplashElement array): + +1. COMPANY LOGO — 2.5s + ├─ FadeIn: 0.5s + ├─ Display: 1.5s + ├─ FadeOut: 0.5s + └─ Texture: T_StudioLogo + +2. UE5 LOGO — 1.5s + ├─ FadeIn: 0.3s + ├─ Display: 1.0s + └─ FadeOut: 0.2s + +3. GAME TITLE — 3.5s + ├─ FadeIn: 0.8s + ├─ Display: 2.5s + │ └─ "PROJECT VOID" — large, with slow glitch effect + ├─ FadeOut: 0.2s + └─ Audio: deep bass rumble begins + +4. HEALTH WARNING — 2.0s + ├─ FadeIn: 0.5s + ├─ Display: 1.0s + │ └─ "This game contains psychological horror, + │ disturbing imagery, and themes of + │ mental illness. Player discretion advised." + ├─ FadeOut: 0.5s + └─ Can be skipped + +5. AUTO-ADVANCE + ├─ Dispatch OnSequenceComplete + └─ Level Blueprint → OpenLevel("L_MainMenu") +``` + +### Override + +``` +Override: PlaySplashSequence + │ + ├─ Parent: PlaySplashSequence + │ + └─ [Horror Additions] + ├─ SS_AudioManager.PlayMusic("splash_ambient", MS_MusicBus) + │ └─ Starts at 0.0 volume, fades to 0.5 over 2 seconds + │ + └─ [Accessibility] + ├─ If SS_SettingsSystem.GetBool("subtitles_enabled"): + │ └─ Show subtitle text for health warning + └─ If SS_SettingsSystem.GetBool("slow_mode"): + └─ Double all display durations +``` + +--- + +## 6. WBP_JournalHorror — Document Viewer + +**Parent:** `WBP_JournalDocumentViewer` (50) | **Asset Path:** `Content/Game/UI/WBP_JournalHorror.uasset` + +### Visual Design + +``` +┌─────────────────────────────────────┐ +│ ┌───────────────────────────────┐ │ +│ │ │ │ +│ │ [AGED PAPER TEXTURE BG] │ │ +│ │ │ │ +│ │ Patient File #47 │ │ +│ │ ───────────────────── │ │ +│ │ │ │ +│ │ "The subject exhibits │ │ +│ │ extreme paranoia and │ │ +│ │ claims to hear voices │ │ +│ │ from within the walls..." │ │ +│ │ │ │ +│ │ ── Dr. Blackwood, 1923 │ │ +│ │ │ │ +│ │ [← PREV] [NEXT →] │ │ +│ └───────────────────────────────┘ │ +│ │ +│ [CLOSE] [FLAG IMPORTANT] │ +└─────────────────────────────────────┘ +``` + +### Override Behavior + +``` +Override: ShowDocument(DocumentData) + │ + ├─ Parent: ShowDocument (renders pages) + │ + └─ [Horror Styling] + ├─ Page background: T_Parchment texture (aged paper) + ├─ Font: Typewriter style, slightly uneven (random kerning) + ├─ Ink color: Dark brown (#3B2314) not pure black + ├─ Page edge: Slight vignette around border + │ + ├─ [Memory Drift Integration] + │ └─ If BPC_StressSystem.GetCurrentStressTier() >= Breaking: + │ └─ Some words on page randomly shift/unblur + │ (subtle hallucination bleed) + │ Timer: every 4-6 seconds, shift 1-2 words, revert after 1s + │ + └─ [Accessibility] + └─ If SS_SettingsSystem.GetBool("dyslexia_font"): + └─ Switch to OpenDyslexic font + +Override: FlagDocument(DocumentData) + │ + ├─ BPC_DocumentArchiveSystem.FlagAsImportant(DocumentData) + ├─ WBP_NotificationToast.Show("Document flagged for review") + └─ (Items flagged as important glow faintly in inventory) +``` + +--- + +## 7. WBP_DeathScreen — Death Overlay + +**Parent:** `UserWidget` (custom — no framework parent) | **Asset Path:** `Content/Game/UI/WBP_DeathScreen.uasset` + +### Purpose + +Shown when the player dies (overlay, not a separate level). Handles the death-to-respawn flow and void space entry. + +### Layout + +``` +┌──────────────────────────────────────────────┐ +│ │ +│ │ +│ ┌──────────────┐ │ +│ │ YOU DIED │ │ +│ │ (fade in) │ │ +│ └──────────────┘ │ +│ │ +│ Death Cause: Void Entity │ +│ Chapter: Basement │ +│ Deaths this run: 4 │ +│ Time survived: 00:45:12 │ +│ │ +│ ┌─────────────────────┐ │ +│ │ "The void watches. │ │ +│ │ It always watches."│ │ +│ └─────────────────────┘ │ +│ │ +│ [CONTINUE] [MAIN MENU] │ +│ (respawn at (quit to │ +│ checkpoint) menu) │ +│ │ +│ (if void-qualifying death shows instead:) │ +│ [ENTER THE VOID] │ +│ │ +└──────────────────────────────────────────────┘ +``` + +### Variables + +| Variable | Type | Default | Purpose | +|----------|------|---------|---------| +| `DeathCause` | Text | — | Set by BPC_DeathHandlingSystem before show | +| `DeathChapter` | Text | — | Current chapter at time of death | +| `DeathsThisRun` | Int | 0 | From PS_HorrorPlayerState | +| `TimeSurvived` | Text | "" | Formatted playtime | +| `bCanEnterVoid` | Boolean | false | True if void space conditions met | +| `RandomFlavorTexts` | Array\ | — | Pool of death-screen quotes | + +### Functions + +``` +ShowDeathScreen(DeathCause: Text, bVoidQualified: Boolean) + │ + ├─ Set DeathCause, DeathChapter, DeathsThisRun, TimeSurvived + │ + ├─ [Fade In] + │ ├─ SetRenderOpacity(0.0) + │ └─ Timeline: 0.0 → 1.0 over 2.0 seconds + │ + ├─ [Visual Effects] + │ ├─ Background: dark red gradient (bottom) to black (top) + │ ├─ Vignette: heavy edge darkening + │ └─ Particle: faint void particles drifting upward + │ + ├─ [Audio] + │ ├─ SS_AudioManager.StopAllSFX() + │ ├─ SS_AudioManager.FadeOutMusic(1.0) + │ └─ SS_AudioManager.PlaySFX("death_sting") + │ └─ Low bass rumble + heartbeat stop + │ + ├─ [Flavor Text] + │ ├─ Select random quote from RandomFlavorTexts + │ └─ Display in center with slow fade-in + │ + ├─ [Button Visibility] + │ ├─ bVoidQualified? → Branch + │ │ ├─ True: Show "ENTER THE VOID" (primary) + │ │ │ + "Continue" (secondary, below) + │ │ └─ False: Show "CONTINUE" (primary) + │ │ + │ └─ Always show "MAIN MENU" (secondary) + │ + └─ [Accessibility] + └─ If SS_SettingsSystem.GetBool("reduce_jumpscares"): + └─ Skip death sting audio, play soft tone instead + +OnContinueClicked + │ + ├─ GM_HorrorGameMode.HandlePlayerDead → resume respawn flow + ├─ HideDeathScreen() + └─ (BPC_PlayerRespawnSystem handles the actual respawn) + +OnEnterVoidClicked + │ + ├─ GM_HorrorGameMode → bVoidSpaceActive = true + ├─ BPC_AltDeathSpaceSystem.EnterVoidSpace() + ├─ TransitionToChapter(Chapter.VoidSpace) + └─ HideDeathScreen() + +OnMainMenuClicked + │ + ├─ Show confirmation: "Return to main menu? All unsaved progress lost." + │ ├─ Yes → OpenLevel("L_MainMenu") + │ └─ No → close confirmation + └─ HideDeathScreen() (called by hide function) +``` + +### Flavor Text Pool + +| Quote | +|-------| +| "The void does not forget. It merely waits." | +| "Each death is a memory. Each memory is a key." | +| "You've been here before. You'll be here again." | +| "The asylum remembers what you cannot." | +| "Death is not an ending. It is a doorway." | +| "Breathe. The darkness is patient." | +| "They say the patients never truly die here." | +| "You are one step closer to the truth." | + +--- + +## 8. WBP_LoadingHorror — Loading Screen + +**Parent:** Associated with `BPC_LoadingScreen` (110) | **Asset Path:** `Content/Game/UI/WBP_LoadingHorror.uasset` + +### Layout + +``` +┌─────────────────────────────────────────────┐ +│ │ +│ │ +│ ┌─────────────────────┐ │ +│ │ │ │ +│ │ LOADING... │ │ +│ │ │ │ +│ │ ████████░░░░ 65% │ │ +│ │ │ │ +│ └─────────────────────┘ │ +│ │ +│ CHAPTER NAME: Ward A — Patient Wing │ +│ │ +│ ┌─────────────────────────────────────┐ │ +│ │ "Darkness is your ally. │ │ +│ │ Enemies can't see what they │ │ +│ │ can't hear." │ │ +│ └─────────────────────────────────────┘ │ +│ │ +│ [void symbol] │ +│ │ +└─────────────────────────────────────────────┘ +``` + +### Variables + +| Variable | Type | Default | Purpose | +|----------|------|---------|---------| +| `TipsDataTable` | DataTable | `DT_LoadingTips` | Pool of loading tips | +| `TipTimer` | Float | `8.0` | Seconds between tip rotation | +| `CurrentTipIndex` | Int32 | `0` | Which tip is showing | +| `BackgroundImage` | Texture | — | Blurred screenshot of current area | + +### Functions + +``` +ShowLoadingScreen(ChapterTag: GameplayTag) + │ + ├─ [Set Chapter Name] + │ ├─ GM_HorrorGameMode → HorrorLevelNames → get name from tag + │ └─ Set ChapterNameText + │ + ├─ [Select Background] + │ └─ Load background texture based on ChapterTag + │ ├─ Entry → "asylum_cell_blur" + │ ├─ WardA → "ward_a_hallway_blur" + │ ├─ WardB → "ward_b_courtyard_blur" + │ ├─ Basement → "basement_dark_blur" + │ └─ Void → "void_abstract" + │ + ├─ [Start Tip Rotation] + │ ├─ Pick random tip from TipsDataTable + │ └─ Set Timer (8.0s, looping) → ShowNextTip + │ + ├─ [Progress Bar] + │ └─ Bind to level load progress (Get Async Load Percentage) + │ └─ Update ProgressBar percent every tick + │ + ├─ [Audio] + │ ├─ SS_AudioManager.PlaySFX("loading_ambient_loop") + │ └─ SS_AudioManager.FadeOutMusic(0.5) + │ + └─ [Add to Viewport] + +HideLoadingScreen() + │ + ├─ Stop TipTimer + ├─ SS_AudioManager.StopSFX("loading_ambient_loop") + ├─ Remove from viewport + └─ Broadcast OnLoadingComplete +``` + +--- + +## UI Integration with Framework Systems + +``` +SS_UIManager (44) — Owns all menu widgets, pushes/pops stack + │ + ├─ MAIN MENU flow: WBP_GameMainMenu → (Settings) → WBP_SettingsMenu + │ WBP_GameMainMenu → (Credits) → WBP_CreditsScreen + │ + ├─ PAUSE flow: IA_PauseMenu → Push WBP_GamePauseMenu + │ └─ (Settings) → Push WBP_SettingsMenu + │ + ├─ INVENTORY flow: IA_OpenWatch → Push WBP_GameInventoryMenu + │ └─ (Read Document) → Push WBP_JournalHorror + │ + └─ DEATH flow: BPC_DeathHandlingSystem → Show WBP_DeathScreen + (NOT through SS_UIManager — direct overlay) + +WBP_HUDController (47) → WBP_GameHUDController + │ + ├─ Always visible during InGame phase + ├─ Hides during menu push (SS_UIManager coordinates) + └─ Children: DiegeticHUDFrame, InteractionPrompt, Objective, Toast, ScreenEffect + +SS_EnhancedInputManager (128) + │ + ├─ Coordinates with SS_UIManager for input mode changes + │ ├─ Menu open → SetInputModeUIOnly → show cursor + │ └─ Menu close → SetInputModeGameOnly → hide cursor + │ + └─ IA_PauseMenu → SS_UIManager + IA_OpenWatch → SS_UIManager + push WristwatchUI context +``` + +--- + +## Accessibility Requirements + +All game UI widgets must implement: + +| Feature | Implementation | Target | +|---------|---------------|--------| +| **Subtitles** | `WBP_AccessibilityUI` — shows all dialogue, whispered voices | Deaf/HoH | +| **Colorblind modes** | 3 presets (Protanopia, Deuteranopia, Tritanopia) — adjust health/stress colors | Colorblind | +| **High contrast** | Toggle via Settings — increases HUD opacity to 0.9, brightens colors | Low vision | +| **Dyslexia font** | Toggle via Settings — switch all text to OpenDyslexic | Dyslexia | +| **Controller support** | All menus navigable via D-Pad + A/B buttons | Console players | +| **Text size** | Slider (Small/Medium/Large) — scales all UI text | Low vision | +| **Reduce jumpscares** | Toggle — replaces stingers with soft tones, removes screen shake | Anxiety/PTSD | +| **Hold-to-confirm** | All destructive actions (Quit, Delete Save) require 1s hold | Accidental press | + +--- + +## Blueprint Wiring Checklist + +- [ ] Create `WBP_GameHUDController` — child of `WBP_HUDController` — set horror colors/fonts +- [ ] Create `WBP_GameMainMenu` — child of `WBP_MainMenu` — Project Void branding +- [ ] Create `WBP_GamePauseMenu` — child of `WBP_PauseMenu` — session stats display +- [ ] Create `WBP_GameInventoryMenu` — child of `WBP_InventoryMenu` — parchment styling +- [ ] Create `WBP_SplashHorror` — child of `WBP_SplashScreen` — boot sequence +- [ ] Create `WBP_JournalHorror` — child of `WBP_JournalDocumentViewer` — aged paper +- [ ] Create `WBP_DeathScreen` — custom UserWidget — death/respawn flow +- [ ] Create `WBP_LoadingHorror` — associated with `BPC_LoadingScreen` — tips + progress +- [ ] Create `DT_LoadingTips` Data Table — 10 horror-themed tips +- [ ] Wire all widgets into `SS_UIManager` menu stack +- [ ] Test all accessibility toggles work through `WBP_SettingsMenu` +- [ ] Verify UI layer hides/shows correctly with game phase changes + +--- + +*UI Overrides for Project Void. See [GAMEINDEX.md](GAMEINDEX.md) for full game structure. See [hud-overview.md](../architecture/hud-overview.md) for HUD architecture. See individual framework widget specs in [06-ui/](../blueprints/06-ui/).* diff --git a/docs/game/weapons-index.md b/docs/game/weapons-index.md new file mode 100644 index 0000000..bf8ac6d --- /dev/null +++ b/docs/game/weapons-index.md @@ -0,0 +1,316 @@ +# Weapons Index — All Held Weapon Actors + +**Game:** Project Void | **Build Phase:** 10 +**Framework Systems:** 69_BP_WeaponBase, 70_BPC_AmmoComponent, 74_BPC_FirearmSystem, 76_BPC_MeleeSystem, 77_BPC_RecoilSystem, 78_BPC_ReloadSystem + +--- + +## Purpose + +Defines the 4 held weapon/tool actors used by the player. Each is spawned by `BPC_EquipmentSlotSystem` when an item is equipped and attached to the player's hand socket. + +--- + +## Weapon Catalog + +| # | Actor | Type | Damage | Fire Rate | Mag Size | Slot | Interfaces | +|---|-------|------|:---:|:---:|:---:|------|------------| +| 1 | `BP_Pistol_Held` | Firearm | 15 | 0.15s (hitscan) | 12 | PrimaryWeapon | I_UsableItem | +| 2 | `BP_Shotgun_Held` | Firearm | 45 (total) | 1.0s (pellets) | 2 | PrimaryWeapon | I_UsableItem | +| 3 | `BP_Flashlight_Held` | Tool | 0 | — | — | Tool | I_UsableItem, I_Toggleable | +| 4 | `BP_Crowbar_Held` | Melee/Tool | 20 | 0.8s (swing) | — | Tool | I_UsableItem, I_MeleeWeapon | + +--- + +## 1. BP_Pistol_Held (Full build in [item-pistol.md](item-pistol.md)) + +**Quick Reference:** + +``` +Components: + ├─ SkeletalMeshComponent (slide animation) + ├─ SceneComponent "MuzzleSocket" + ├─ AudioComponent "FireSound" + ├─ AudioComponent "ReloadSound" + └─ ParticleSystemComponent "MuzzleFlash" + +Core Loop: + IA_Fire → UseItem() → Ammo check → Hitscan → Damage → Recoil → Timer lockout + IA_Reload → Reload() → BPC_AmmoComponent.ConsumeAmmo() → update magazine + +States: + ├─ Idle (can fire) + ├─ Firing (timer lockout) + ├─ Empty (out of ammo — click sound) + └─ Reloading (animation, blocked input) +``` + +--- + +## 2. BP_Shotgun_Held + +**Parent:** `BP_WeaponBase` (69) + +### Components + +| # | Component | Name | Purpose | +|---|-----------|------|---------| +| 1 | `SkeletalMeshComponent` | `WeaponMesh` | Break-action double barrel | +| 2 | `SceneComponent` | `MuzzleSocket` | End of barrel | +| 3 | `AudioComponent` | `FireSound` | Loud shotgun blast | +| 4 | `AudioComponent` | `ReloadSound` | Shell insertion click | +| 5 | `AudioComponent` | `BreakOpenSound` | Opening action sound | +| 6 | `AudioComponent` | `BreakCloseSound` | Closing action snap | +| 7 | `ParticleSystemComponent` | `MuzzleFlash` | Large muzzle flash | +| 8 | `PointLightComponent` | `MuzzleLight` | Brief flash on fire | + +### Variables + +| Variable | Type | Default | Purpose | +|----------|------|---------|---------| +| `CurrentShells` | Int | `2` | Shells currently loaded (0-2) | +| `MaxShells` | Int | `2` | Always 2 for double-barrel | +| `bIsFiring` | Bool | `false` | Fire lockout | +| `bIsReloading` | Bool | `false` | Reload lockout | +| `bIsBrokenOpen` | Bool | `false` | Break-action state | +| `WeaponData` | DA_ItemData* | — | Shotgun Data Asset | +| `PelletCount` | Int | `6` | Pellets per shot | +| `PelletSpreadAngle` | Float | `5.0` | Degrees of spread cone | + +### Fire Logic (Pellet Spread) + +``` +UseItem() → (called by IA_Fire or IA_Fire+IA_Aim for ADS) + │ + ├─ bIsFiring? → Return False + ├─ bIsReloading? → Return False + ├─ bIsBrokenOpen? → Auto-close barrels → wait 0.3s + │ + ├─ CurrentShells > 0? → Branch + │ ├─ True: + │ │ ├─ Set bIsFiring = true + │ │ ├─ CurrentShells -= 1 + │ │ │ + │ │ ├─ [Fire Both Barrels if 2 shells loaded?] + │ │ │ └─ Designer option: single or double fire + │ │ │ └─ Default: single fire (double on alt-fire) + │ │ │ + │ │ ├─ [Multi-Trace Pellet System] + │ │ │ │ + │ │ │ ├─ For i = 0 to PelletCount: + │ │ │ │ ├─ Calculate spread direction: + │ │ │ │ │ ├─ BaseDir: Camera forward vector + │ │ │ │ │ └─ Apply Random Cone (PelletSpreadAngle) + │ │ │ │ │ + │ │ │ │ ├─ Start: MuzzleSocket.GetWorldLocation + │ │ │ │ ├─ End: Start + SpreadDir * FireRange + │ │ │ │ │ + │ │ │ │ └─ Line Trace By Channel (Visibility) + │ │ │ │ ├─ Hit? → Branch + │ │ │ │ │ ├─ True: + │ │ │ │ │ │ ├─ HitActor → I_Damageable? + │ │ │ │ │ │ │ └─ ApplyDamage(PelletDamage, ...) + │ │ │ │ │ │ └─ Spawn impact decal + │ │ │ │ │ └─ False: continue + │ │ │ │ └─ Draw debug line (editor only, color per pellet) + │ │ │ │ + │ │ │ └─ Track total pellets hit for combat feedback + │ │ │ + │ │ ├─ [Effects] + │ │ │ ├─ MuzzleFlash → Activate + │ │ │ ├─ FireSound → Play + │ │ │ ├─ MuzzleLight → SetVisibility(true) → Delay(0.05) → false + │ │ │ └─ Camera shake (heavy): PlayCameraShake(ShotgunKick) + │ │ │ + │ │ ├─ [Recoil] + │ │ │ └─ BPC_RecoilSystem.ApplyRecoil(WeaponData, ×3 heavy multiplier) + │ │ │ + │ │ ├─ [Post-Fire] + │ │ │ ├─ Delay(FireRateTimer) → Set bIsFiring = false + │ │ │ └─ IF CurrentShells == 0 → Auto-break-open for reload + │ │ │ └─ Set bIsBrokenOpen = true, play break animation + │ │ │ + │ │ └─ Return True + │ │ + │ └─ False: (empty) + │ ├─ Play click/empty sound + │ └─ Return False +``` + +### Reload Logic (Shell-by-Shell) + +``` +Reload() + │ + ├─ CurrentShells >= MaxShells? → Return (full) + ├─ bIsReloading? → Return (already reloading) + │ + ├─ [If not broken open] → Play break-open animation → Set bIsBrokenOpen = true + │ + ├─ Set bIsReloading = true + │ + ├─ [Shell Loop — reload one shell at a time] + │ │ + │ ├─ BPC_AmmoComponent.GetAmmoCount(ShellAmmoTypeTag) + │ │ + │ ├─ While CurrentShells < MaxShells AND BPC_AmmoComponent.AmmoCount > 0: + │ │ ├─ BPC_AmmoComponent.ConsumeAmmo(ShellAmmoTypeTag, 1) + │ │ ├─ CurrentShells += 1 + │ │ ├─ Play ReloadSound (shell click) + │ │ └─ Delay(ShellReloadDelay = 0.8s) + │ │ + │ └─ [Player CAN fire mid-reload if at least 1 shell loaded] + │ └─ If IA_Fire pressed during reload: + │ ├─ Stop reload loop + │ ├─ Play break-close animation + │ ├─ Set bIsBrokenOpen = false + │ ├─ Set bIsReloading = false + │ └─ Process Fire (with whatever shells are loaded) + │ + ├─ [Reload Complete] + │ ├─ Play break-close animation + │ ├─ Set bIsBrokenOpen = false + │ ├─ Set bIsReloading = false + │ └─ Broadcast OnReloadComplete +``` + +### Damage Falloff + +``` +CalculatePelletDamage(BaseDamage, Distance): + │ + ├─ IF Distance <= 500: multiplier = 1.0 (point-blank: devastating) + ├─ IF Distance <= 1500: multiplier = 0.7 (close range: lethal) + ├─ IF Distance <= 2500: multiplier = 0.4 (medium: wounding) + └─ IF Distance > 2500: multiplier = 0.15 (long: minor) + │ + └─ Return BaseDamage × multiplier / PelletCount +``` + +--- + +## 3. BP_Flashlight_Held (Full build in [item-flashlight.md](item-flashlight.md)) + +**Quick Reference:** +- Implements `I_UsableItem` and `I_Toggleable` +- UseItem() → toggles light on/off +- Battery consumed at 2%/second while on +- Battery restored by using `DA_Item_Battery` from consumable system +- SpotLightComponent with Cone Angle: 15° / 30° + +--- + +## 4. BP_Crowbar_Held + +**Parent:** `Actor` | **Implements:** `I_UsableItem`, `I_MeleeWeapon` + +### Components + +| # | Component | Name | Purpose | +|---|-----------|------|---------| +| 1 | `StaticMeshComponent` | `CrowbarMesh` | Rusted crowbar model | +| 2 | `BoxComponent` | `SwingHitbox` | Melee hit detection (disabled until swing) | + +### Variables + +| Variable | Type | Default | Purpose | +|----------|------|---------|---------| +| `SwingDamage` | Float | `20.0` | Damage per swing | +| `SwingCooldown` | Float | `0.8` | Seconds between swings | +| `bIsSwinging` | Bool | `false` | Swing lockout | +| `SwingRange` | Float | `250.0` | Units of reach | +| `SwingArc` | Float | `90.0` | Degrees of arc | + +### Melee Logic + +``` +UseItem() → BPC_MeleeSystem.Swing(Self) + │ + ├─ bIsSwinging? → Return False + │ + ├─ Set bIsSwinging = true + │ + ├─ [Swing Animation] + │ └─ Timeline (0.0 → 1.0 over 0.3s) → Update crowbar rotation + │ + ├─ [Swing Hitbox — enable at 0.15s (mid-swing)] + │ ├─ Delay(0.15) → SwingHitbox.SetCollisionEnabled(QueryOnly) + │ ├─ OnComponentBeginOverlap(SwingHitbox, OtherActor): + │ │ ├─ OtherActor → DoesImplementInterface(I_Damageable)? + │ │ │ ├─ True: + │ │ │ │ ├─ ApplyDamage(SwingDamage, Self, Melee, HitLocation, HitDirection) + │ │ │ │ ├─ Play hit sound (metal thud) + │ │ │ │ └─ BPC_CombatFeedbackComponent.ShowHitMarker() + │ │ │ └─ False: Play wall/object hit sound + │ │ └─ (Hit each actor only once per swing — use a hit-set) + │ │ + │ └─ Delay(0.15) → SwingHitbox.SetCollisionEnabled(NoCollision) + │ + ├─ [Swing Complete] + │ ├─ Delay(SwingCooldown) → Set bIsSwinging = false + │ └─ Return True +``` + +### Pry Function + +``` +CanPryOpen(DoorActor: BP_DoorActor*) → Boolean + └─ Return (DoorActor.IsBarricaded()) + +PryDoor(DoorActor: BP_DoorActor*) + ├─ DoorActor.Open() (forces open, breaks barricade) + ├─ Play wood/metal break sound + ├─ DoorActor.SetLocked(false) permanently + └─ SS_AudioManager.PlaySFX("crowbar_pry") +``` + +--- + +## Weapon Animation Notifies (GASP Integration) + +All held weapons fire animation notifies that the GASP AnimBP listens to: + +| Notify | Weapon | Purpose | +|--------|--------|---------| +| `Notify_Fire` | Pistol, Shotgun | Triggers fire animation blend | +| `Notify_ReloadStart` | Pistol, Shotgun | Enters reload animation state | +| `Notify_ReloadEnd` | Pistol, Shotgun | Exits reload animation state | +| `Notify_MeleeSwing` | Crowbar | Triggers melee swing blend | +| `Notify_Equip` | All | Weapon equip animation | +| `Notify_Unequip` | All | Weapon holster animation | +| `Notify_AimDownSights` | Pistol, Shotgun | ADS pose transition | +| `Notify_HipFire` | Pistol, Shotgun | Hip fire pose transition | + +These are added to montage slots in the **GASP child AnimBP**, NOT in the weapon BP itself. + +--- + +## Quick Comparison + +| Feature | Pistol | Shotgun | Flashlight | Crowbar | +|---------|--------|---------|-----------|---------| +| **Role** | Ranged DPS | Burst damage | Vision | Utility + Melee | +| **Best Range** | Medium-Long | Close | Infinite (light) | Melee | +| **Ammo Management** | Magazine | Shell-by-shell | Battery (%) | None | +| **Reload Strategy** | Dump + reload | Tactical (can interrupt) | Replace battery | N/A | +| **Recoil** | Light | Heavy | None | None | +| **ADS Benefit** | Higher accuracy | Tighter spread | N/A | N/A | +| **Stress Impact** | None | +5 (loud) | -1/sec (safe light) | None | +| **Silent?** | No | No | Yes | No (swing noise) | +| **Attracts Enemies?** | Yes (2000u radius) | Yes (3000u radius) | Yes (800u radius) | Yes (1200u radius) | + +--- + +## Notes for Expansion + +- Add **weapon degradation**: pistol jams after 100 shots without cleaning +- Add **flashlight flicker**: battery below 10% causes random light flicker (horror mechanic) +- Add **shotgun slug ammo**: alternate ammo type — single slug instead of pellets, longer range +- Add **crowbar upgrade**: wrap with cloth → silent melee swings (no enemy alert) +- Add **aim-assist** for controller players (configurable in accessibility) +- Add **weapon inspection animation**: hold reload key to inspect weapon (cosmetic) +- Multiplayer: all fire/reload must be server-authoritative with client prediction for effects + +--- + +*Weapons Index for Project Void. See [GAMEINDEX.md](GAMEINDEX.md) for full game structure. See [item-pistol.md](item-pistol.md) and [item-flashlight.md](item-flashlight.md) for detailed build walkthroughs.*