# 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.*