# 49 — BP_WeaponBase

## Blueprint Spec — UE 5.5–5.7

---

### Parent Class
`Actor` (attached to character via `AttachToComponent`)

### Dependencies
- [`BPC_InventoryComponent`](../04-inventory/25_BPC_InventoryComponent.md)
- [`BPC_EquipmentSystem`](../04-inventory/28_BPC_EquipmentSystem.md)
- [`BPC_PlayerCameraManager`](../02-player/14_BPC_PlayerCameraManager.md)
- [`BPC_DamageHandler`](53_BPC_DamageHandlerComponent.md)
- [`DA_WeaponData`](../12-content/60_DA_WeaponDataAsset.md)
- [`I_Damageable`](../01-core/03_I_Damageable.md)
- [`BPC_CombatFeedbackComponent`](54_BPC_CombatFeedbackComponent.md)

### Purpose
Base class for all weapon actors (ranged and melee). Provides common weapon lifecycle: equip, unequip, fire input, ammo tracking integration, aim-down-sights, and weapon state machine. Designed to be extended by [`BP_RangedWeapon`](50_BP_RangedWeapon.md) and [`BP_MeleeWeapon`](51_BP_MeleeWeapon.md).

### Responsibilities
- Attach/detach from character socket on equip/unequip
- Manage weapon state (holstered / equipping / ready / firing / reloading)
- Handle fire input (start / stop) — base delegates to subclass
- Track aim-down-sights state and apply camera FOV transition
- Fire event dispatchers for UI state changes
- Communicate with [`BPC_EquipmentSystem`](../04-inventory/28_BPC_EquipmentSystem.md) for weapon selection
- Communicate with inventory for ammo consumption

### Does NOT Handle
- Specific fire logic (raycast / projectile / melee hitbox) — that is subclass
- Ammo math (that is [`BPC_AmmoComponent`](52_BPC_AmmoComponent.md))
- Damage calculation (that is [`BPC_DamageHandler`](53_BPC_DamageHandlerComponent.md))
- Visual / audio feedback (that is [`BPC_CombatFeedbackComponent`](54_BPC_CombatFeedbackComponent.md))
- UI display (that is HUD widget)

### Variables

| Name | Type | Description |
|------|------|-------------|
| `WeaponData` | DA_WeaponData | Weapon definition asset |
| `WeaponState` | EWeaponState | Current state machine |
| `SkeletalMesh` | USkeletalMeshComponent | Weapon visual |
| `WeaponRoot` | USceneComponent | Attachment root |
| `HolsterSocket` | FName | Socket name for holstered |
| `EquipSocket` | FName | Socket name for ready |
| `AimDownSightsFOV` | Float | FOV when ADS (e.g. 60.0) |
| `DefaultFOV` | Float | Normal FOV |
| `ADSInterpSpeed` | Float | Camera blend speed |
| `bIsAiming` | Bool | Currently ADS |
| `FireRateTimer` | FTimerHandle | Fire rate control |
| `bCanFire` | Bool | Not on cooldown |
| `EquipDuration` | Float | Seconds for equip animation |
| `HolsterDuration` | Float | Seconds for holster animation |
| `RecoilPitch` | Float | Camera pitch per shot |
| `RecoilYaw` | Float | Camera yaw per shot |

### Enums

| Enum | Values | Description |
|------|--------|-------------|
| `EWeaponState` | Holstered, Equipping, Ready, Firing, Reloading, Holstering, Broken | Weapon lifecycle |
| `EWeaponFireMode` | SemiAutomatic, FullAutomatic, BurstFire, ChargeAndRelease, MeleeSwing | Fire type |

### Functions / Events

| Name | Inputs | Outputs | Description |
|------|--------|---------|-------------|
| `Equip` | — | — | Play equip anim, attach to EquipSocket, set state to Ready |
| `Holster` | — | — | Play holster anim, attach to HolsterSocket, set state to Holstered |
| `StartFire` | — | — | Begin fire loop (if auto) or single fire (semi) |
| `StopFire` | — | — | End fire loop |
| `CanFire` | — | Bool | Check state, ammo, cooldown |
| `OnFire` | — | — | Virtual: subclass implements actual fire logic |
| `StartReload` | — | — | Begin reload sequence |
| `FinishReload` | — | — | Called after reload anim |
| `StartAim` | — | — | Blend camera to ADS FOV |
| `StopAim` | — | — | Blend camera back to DefaultFOV |
| `SetWeaponState` | NewState: EWeaponState | — | Change state and broadcast |
| `GetWeaponTag` | — | GameplayTag | Return weapon identifier |
| `OnWeaponBroken` | — | — | Weapon disabled (durability zero) |

### Event Dispatchers

| Name | Parameters | Fired When |
|------|-----------|-----------|
| `OnWeaponStateChanged` | NewState: EWeaponState, OldState: EWeaponState | State machine change |
| `OnWeaponEquipped` | WeaponRef: BP_WeaponBase | Ready to use |
| `OnWeaponHolstered` | WeaponRef: BP_WeaponBase | Stored away |
| `OnFireStarted` | — | Fire input received and valid |
| `OnFireStopped` | — | Fire input released |
| `OnAimStarted` | — | ADS enter |
| `OnAimStopped` | — | ADS exit |
| `OnReloadStarted` | — | Reload begin |
| `OnReloadCompleted` | — | Reload finish |
| `OnWeaponBroken` | — | Durability depleted |

### Blueprint Flow

```
[StartFire]
  └─► If !CanFire → return
  └─► Set WeaponState = Firing
  └─► Broadcast OnFireStarted
  └─► Call OnFire (subclass override)
  └─► If FireMode == FullAutomatic → start FireRateTimer loop
  └─► If FireMode == SemiAutomatic → block until timer clears

[StopFire]
  └─► Clear FireRateTimer
  └─► If FireMode == BurstFire → let burst complete, then stop
  └─► Set WeaponState = Ready
  └─► Broadcast OnFireStopped

[StartReload]
  └─► If WeaponState != Ready → return
  └─► Set WeaponState = Reloading
  └─► Broadcast OnReloadStarted
  └─► Start animation or timer for reload duration
  └─► On completion → FinishReload

[FinishReload]
  └─► BPC_AmmoComponent.RefillAmmo(WeaponData.MagazineSize)
  └─► Set WeaponState = Ready
  └─► Broadcast OnReloadCompleted

[StartAim / StopAim]
  └─► Set bIsAiming
  └─► Get BPC_PlayerCameraManager from owner
  └─► Set FOV with interp speed
  └─► Broadcast OnAimStarted / OnAimStopped
```

### Communications With

| Target System | Method | Why |
|---------------|--------|-----|
| [`BPC_EquipmentSystem`](../04-inventory/28_BPC_EquipmentSystem.md) | Direct | Get/return weapon socket assignment |
| [`BPC_AmmoComponent`](52_BPC_AmmoComponent.md) | Get Component | Check / consume / refill ammo |
| [`BPC_DamageHandler`](53_BPC_DamageHandlerComponent.md) | Get Component | Delegate damage processing |
| [`BPC_CombatFeedbackComponent`](54_BPC_CombatFeedbackComponent.md) | Get Component | Play muzzle flash, hit FX, sounds |
| [`BPC_PlayerCameraManager`](../02-player/14_BPC_PlayerCameraManager.md) | Get from Owner | ADS FOV blend |
| [`BPC_InventoryComponent`](../04-inventory/25_BPC_InventoryComponent.md) | Get from Owner | Check ammo inventory |
| HUD Widget | Dispatcher (via GM) | Update crosshair, ammo display |
| Owner (Player Character) | Direct | Animation blueprint queries weapon state |

### Reuse Notes
Subclass for ranged or melee specific fire implementations. The state machine prevents firing during reload, equip, or holster sequences. Weapons are data-driven via `DA_WeaponData` — design changes never require blueprint edits.

---

## Manual Implementation Guide

### Class Setup
1. Create Blueprint Class: Parent = `Actor`, Name = `BP_WeaponBase`
2. Save to: `Content/Framework/Weapons/`
3. Add components in Construction Script:
   - `WeaponRoot` (SceneComponent) — root
   - `SkeletalMesh` (SkeletalMeshComponent) — attached to root
4. Set `Replicates` = true in Class Defaults

### Variable Defaults (Class Defaults)
| Variable | Type | Default |
|----------|------|---------|
| `WeaponState` | `EWeaponState` | `Holstered` |
| `HolsterSocket` | `Name` | `weapon_holster` |
| `EquipSocket` | `Name` | `weapon_equip` |
| `AimDownSightsFOV` | `Float` | `55.0` |
| `DefaultFOV` | `Float` | `90.0` |
| `ADSInterpSpeed` | `Float` | `10.0` |
| `bIsAiming` | `Boolean` | `false` |
| `bCanFire` | `Boolean` | `true` |
| `EquipDuration` | `Float` | `0.5` |
| `HolsterDuration` | `Float` | `0.5` |
| `RecoilPitch` | `Float` | `2.0` |
| `RecoilYaw` | `Float` | `0.5` |

### Function Implementations

#### `Equip(CharacterOwner: Character)` → `void`

```
[Function: Equip]
  Step 1: Branch on WeaponState == Holstered → If not, return (can't equip twice)
  Step 2: SetWeaponState(Equipping)
  Step 3: AttachToComponent(CharacterOwner.Mesh, EquipSocket)
    - Set Relative Location = (0,0,0)
    - Set Relative Rotation = (0,0,0)
  Step 4: Set Actor Hidden In Game = false
  Step 5: Play equip animation on SkeletalMesh (if montage exists)
  Step 6: Start timer for EquipDuration:
    └─ On timer complete:
      - SetWeaponState(Ready)
      - Set bCanFire = true
      - Fire OnWeaponEquipped(Self)
```

**Nodes:** `Switch on EWeaponState`, `Attach Actor to Component`, `Set Actor Hidden In Game`, `Play Animation`, `Set Timer by Event`

#### `Holster()` → `void`

```
[Function: Holster]
  Step 1: Branch on WeaponState == Ready OR Firing → If not, return
  Step 2: If WeaponState == Firing: StopFire first
  Step 3: SetWeaponState(Holstering)
  Step 4: Play holster animation on SkeletalMesh
  Step 5: Start timer for HolsterDuration:
    └─ On timer complete:
      - AttachToComponent(CharacterOwner.Mesh, HolsterSocket)
      - SetWeaponState(Holstered)
      - Fire OnWeaponHolstered(Self)
```

#### `StartFire()` → `void`

```
[Function: StartFire]
  Step 1: Branch on WeaponState == Ready (only fire from Ready state):
    False → Return
  Step 2: Call CanFire() → Branch on result:
    False → Return
  Step 3: SetWeaponState(Firing)
  Step 4: Fire OnFireStarted
  Step 5: Call OnFire() — this is the subclass override point (virtual function)
  Step 6: Switch on WeaponData.FireMode:
    Case SemiAutomatic:
      - Set bCanFire = false
      - Set timer by WeaponData.FireRate → on complete: Set bCanFire = true
    Case FullAutomatic:
      - Start looping timer (interval = WeaponData.FireRate)
      - Each tick: Call OnFire()
    Case BurstFire:
      - Loop 3 times with WeaponData.FireRate delay between each
      - On burst complete: SetWeaponState(Ready)
```

**Important:** `OnFire()` is a **BlueprintImplementableEvent** — it has NO body in BP_WeaponBase. Subclasses (BP_RangedWeapon, BP_MeleeWeapon) override it with their own fire logic.

#### `StopFire()` → `void`

```
[Function: StopFire]
  Step 1: Branch on WeaponState == Firing:
    False → Return
  Step 2: If FireMode == FullAutomatic OR BurstFire:
    - Clear all FireRate timers
  Step 3: SetWeaponState(Ready)
  Step 4: Fire OnFireStopped
```

#### `CanFire()` → `Boolean`

```
[Function: CanFire] (Pure)
  Step 1: Branch on WeaponState == Ready → If not, return false
  Step 2: Branch on bCanFire → If not, return false (on cooldown)
  Step 3: Get AmmoComponent from Owner → Call HasAmmo()
    - If no ammo: return false (subclass may trigger auto-reload)
  Step 4: Get Owner → Get BPC_StateManager → Call IsActionPermitted(GameplayTag: "Action.Fire")
    - If not permitted: return false
  Step 5: Return true
```

#### `StartReload()` → `void`

```
[Function: StartReload]
  Step 1: Branch on WeaponState == Ready:
    False → Return (can't reload while firing/holstered)
  Step 2: Branch on AmmoComponent.IsMagazineFull():
    True → Return (no need to reload)
  Step 3: Branch on AmmoComponent.HasReserveAmmo():
    False → Return (no ammo to load)
  Step 4: SetWeaponState(Reloading)
  Step 5: Fire OnReloadStarted
  Step 6: Play reload animation montage:
    - Use AnimNotify "OnReloadComplete" at the ammo-insert frame
    - AnimNotify handler: call FinishReload()
```

#### `FinishReload()` → `void`

```
[Function: FinishReload]
  Step 1: Get AmmoComponent → Call RefillAmmo(MagazineSize from WeaponData)
  Step 2: SetWeaponState(Ready)
  Step 3: Fire OnReloadCompleted
```

#### `StartAim()` → `void`

```
[Function: StartAim]
  Step 1: Set bIsAiming = true
  Step 2: Get Owner → Get PlayerController → Get PlayerCameraManager
  Step 3: Set PlayerCameraManager Field of View to AimDownSightsFOV with interp speed
  Step 4: Fire OnAimStarted
```

#### `StopAim()` → `void`

```
[Function: StopAim]
  Step 1: Set bIsAiming = false
  Step 2: Get PlayerCameraManager → Set FOV to DefaultFOV with interp
  Step 3: Fire OnAimStopped
```

#### `SetWeaponState(NewState: EWeaponState)` → `void`

```
[Function: SetWeaponState]
  Step 1: OldState = WeaponState
  Step 2: If NewState == OldState → Return
  Step 3: WeaponState = NewState
  Step 4: Mark variable dirty (for replication)
  Step 5: Fire OnWeaponStateChanged(NewState, OldState)
```

### Event Dispatcher Bindings

| Bind to Dispatcher | Custom Event | Logic |
|-------------------|-------------|-------|
| `IA_Fire` (Pressed) | `OnFirePressed` | Call StartFire() |
| `IA_Fire` (Released) | `OnFireReleased` | Call StopFire() |
| `IA_Reload` (Pressed) | `OnReloadPressed` | Call StartReload() |
| `IA_Aim` (Pressed) | `OnAimPressed` | Call StartAim() |
| `IA_Aim` (Released) | `OnAimReleased` | Call StopAim() |

### Networking
- **WeaponState** replicated with `OnRep_WeaponState` → plays matching animation on remote clients
- **All state transitions** are server-authoritative via `Server_StartFire`, `Server_StartReload`, etc.
- Client predicts: fire animation, muzzle flash, recoil (cosmetic only)
- Server validates: ammo count, fire rate cooldown, state machine transitions

### Blueprint Build Checklist
- [ ] Create BP_WeaponBase actor with WeaponRoot + SkeletalMesh components
- [ ] Add EquipmentSocket component on Player Character (weapon_equip, weapon_holster)
- [ ] Add all variables with correct types and defaults
- [ ] Implement Equip/Holster with attach/detach and animation timers
- [ ] Implement StartFire/StopFire with fire rate timer loop
- [ ] Create OnFire as BlueprintImplementableEvent (empty — subclasses override)
- [ ] Implement CanFire checks: state, cooldown, ammo, StateManager permission
- [ ] Implement StartReload/FinishReload with animation notify
- [ ] Implement StartAim/StopAim with FOV interpolation
- [ ] Implement SetWeaponState with old state tracking and dispatcher
- [ ] Bind all input actions (Fire, Reload, Aim)
- [ ] Add networking: replicated WeaponState, Server_ RPCs for all mutators