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