# 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