- 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.
12 KiB
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)
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)
Quick Reference:
- Implements
I_UsableItemandI_Toggleable - UseItem() → toggles light on/off
- Battery consumed at 2%/second while on
- Battery restored by using
DA_Item_Batteryfrom 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 for full game structure. See item-pistol.md and item-flashlight.md for detailed build walkthroughs.