# Item Example — Pistol (Weapon)

**Item Type:** `Weapon`
**Complexity:** High
**Systems Used:** `DA_ItemData`, `BP_ItemPickup`, `BP_WeaponBase`, `BPC_FirearmSystem`, `BPC_AmmoComponent`, `BPC_DamageReceptionSystem`, `BPC_RecoilSystem`, `BPC_ReloadSystem`, `BPC_EquipmentSlotSystem`, `BPC_InventorySystem`, `I_UsableItem`, `I_Damageable`
**What You Learn:** Full weapon pipeline — Data Asset with EquipmentData, pickup-to-equip flow, frame-driven fire with hitscan, ammo management, damage application, recoil

---

## 1. Create the DA_ItemData

### 1.1 — Create the Data Asset

```
Content Browser → Game/Items/ (or Game/Weapons/)
  Right-click → Miscellaneous → Data Asset
  Class: DA_ItemData
  Name: DA_Item_Pistol
```

### 1.2 — Fill Core Properties

| Property | Value | Notes |
|----------|-------|-------|
| `Item Tag` | `Framework.Item.Weapon.Pistol` | Registered in `DA_GameTagRegistry` |
| `Display Name` | "9mm Pistol" | |
| `Description` | "A standard-issue 9mm semi-automatic pistol." | |
| `Icon` | `T_Pistol_Icon` | |
| `World Mesh` | `SM_Pistol` | Soft reference |
| `Weight` | `3.0` | Heavier than consumables |
| `Stack Limit` | `1` | Cannot stack weapons |
| `Item Type` | `Weapon` | Triggers `Equipment Data` panel |

### 1.3 — Fill Equipment Data

When `ItemType == Weapon`, the `Equipment Data` panel appears:

| Field | Value | Notes |
|-------|-------|-------|
| `Slot` | `Framework.Equipment.Slot.PrimaryWeapon` | Occupies player's primary weapon slot |
| `Damage` | `15.0` | Base damage per shot |
| `Fire Rate` | `0.15` | Seconds between shots (400 RPM) |
| `Range` | `5000.0` | Hitscan range in Unreal units |
| `Magazine Size` | `12` | Rounds per magazine |
| `Reload Time` | `2.0` | Seconds to reload |

### 1.4 — Save

---

## 2. Create the BP_Pickup_Pistol (World Pickup)

Same as other pickups, with weapon mesh:

### 2.1 — Blueprint + Components

```
Content Browser → Game/Pickups/
  Right-click → Blueprint Class → Actor
  Name: BP_Pickup_Pistol

Components:
  ├── StaticMeshComponent "Mesh"
  └── SphereComponent "PickupTrigger" (radius 200, OverlapOnlyPawn)
```

### 2.2 — Variable & Construction Script

Same pattern as flashlight — `ItemData` → Get World Mesh → Load Synchronous → Set Static Mesh.

### 2.3 — I_Interactable Wiring

Same as other pickups, but **after adding item to inventory**, also auto-equip it:

```
Event Interact
  │
  ├─ Interactor → Get BPC_InventorySystem
  ├─ CanAddItem(ItemData, 1) → True → AddItem → store result
  │    │
  │    ├─ Interactor → Get BPC_EquipmentSlotSystem
  │    │    └─ Equip To Slot(ItemData, PrimaryWeapon slot tag)
  │    │         └─ This spawns the BP_Pistol actor at the player's hand socket
  │    │
  │    ├─ Destroy Actor
  │    └─ Return True
  │
  └─ False → Print "Inventory Full" → Return False
```

---

## 3. Create the BP_Pistol (Held Weapon Actor)

This is the actor that appears in the player's hand when equipped.

### 3.1 — Create Blueprint

```
Content Browser → Game/Actors/  (or Game/Weapons/)
  Right-click → Blueprint Class → Actor  (or BP_WeaponBase from framework)
  Name: BP_Pistol
```

### 3.2 — Add Components

| # | Component | Name | Purpose |
|---|-----------|------|---------|
| 1 | `SkeletalMeshComponent` | `WeaponMesh` | Animated weapon (slide, magazine) |
| 2 | `SceneComponent` | `MuzzleSocket` | Where the bullet trace starts (attach to muzzle bone) |
| 3 | `AudioComponent` | `FireSound` | Plays fire sound |
| 4 | `AudioComponent` | `ReloadSound` | Plays reload sound |
| 5 | `ParticleSystemComponent` | `MuzzleFlash` | Fire particle effect |
| 6 | *(Optional)* `PointLightComponent` | `MuzzleLight` | Brief flash on fire |

Parent `MuzzleSocket` to the muzzle bone on `WeaponMesh`, or set its relative location to where the barrel ends.

### 3.3 — Variables

| Variable | Type | Default | Purpose |
|----------|------|---------|---------|
| `CurrentAmmoInMag` | Integer | `12` | Rounds currently loaded |
| `MaxMagazineSize` | Integer | `12` | From EquipmentData |
| `bIsFiring` | Boolean | `false` | Prevents re-trigger during fire rate cooldown |
| `FireRateTimer` | Float | `0.15` | Seconds between shots |
| `WeaponData` | DA_ItemData* | — | Reference to the pistol Data Asset |
| `FireRange` | Float | `5000.0` | Hitscan distance |

### 3.4 — Interfaces

Class Settings → Interfaces → Add:
- `UUsableItem` — so active item system can route fire input

### 3.5 — Fire Logic (Hitscan)

```
Event Use Item (Interface -> UUsableItem)
  │ User: AActor*, Target: AActor*
  │ Return Boolean
  │
  ├─ bIsFiring? → Branch
  │    ├─ True → Return False (fire rate lockout)
  │    └─ False:
  │         │
  │         ├─ CurrentAmmoInMag > 0? → Branch
  │         │    ├─ True:
  │         │    │    ├─ Set bIsFiring = true
  │         │    │    ├─ CurrentAmmoInMag = CurrentAmmoInMag - 1
  │         │    │    │
  │         │    │    ├─ [Play Effects]
  │         │    │    │    ├─ MuzzleFlash → Activate
  │         │    │    │    ├─ FireSound → Play
  │         │    │    │    └─ MuzzleLight → Set Visibility (true)
  │         │    │    │         └─ Delay 0.05 → Set Visibility (false)
  │         │    │    │
  │         │    │    ├─ [Line Trace — Hitscan]
  │         │    │    │    │
  │         │    │    │    ├─ Get Player Controller → Get Camera Location/Direction
  │         │    │    │    ├─ Start: MuzzleSocket → Get World Location
  │         │    │    │    ├─ End: Start + (CameraForward × FireRange)
  │         │    │    │    │
  │         │    │    │    └─ Line Trace By Channel (Visibility)
  │         │    │    │         ├─ Trace Complex: false
  │         │    │    │         └─ Ignore Self, Ignore Owning Pawn
  │         │    │    │              │
  │         │    │    │              ├─ Hit? → Branch
  │         │    │    │              │    │
  │         │    │    │              │    ├─ True:
  │         │    │    │              │    │    ├─ Hit Actor → Does Implement Interface (UDamageable)?
  │         │    │    │              │    │    │    ├─ True:
  │         │    │    │              │    │    │    │    ├─ Call I_Damageable.Take Damage
  │         │    │    │              │    │    │    │    │    ├─ Damage Amount: WeaponData → Get Equipment Data → Damage
  │         │    │    │              │    │    │    │    │    ├─ Damage Causer: Self (or OwningPawn)
  │         │    │    │              │    │    │    │    │    ├─ Damage Type: Framework.Combat.Damage.Physical
  │         │    │    │              │    │    │    │    │    ├─ Hit Location: HitResult.ImpactPoint
  │         │    │    │              │    │    │    │    │    └─ Hit Direction: (ImpactPoint - Start) normalized
  │         │    │    │              │    │    │    │    │
  │         │    │    │              │    │    │    │    └─ Print String: "Hit {HitActor} for {Damage} damage"
  │         │    │    │              │    │    │    │
  │         │    │    │              │    │    │    └─ False: Print "Hit non-damageable object"
  │         │    │    │              │    │    │
  │         │    │    │              │    │    └─ Spawn Decal/Impact Effect at Hit Location
  │         │    │    │              │    │
  │         │    │    │              │    └─ False (miss): nothing
  │         │    │    │              │
  │         │    │    │              └─ Draw Debug Line (red for 1 second, editor only)
  │         │    │    │
  │         │    │    ├─ [Recoil]
  │         │    │    │    └─ Get BPC_RecoilSystem (on pawn) → Apply Recoil (WeaponData)
  │         │    │    │
  │         │    │    └─ Delay (FireRateTimer) → Set bIsFiring = false → Return True
  │         │    │
  │         │    └─ False: (out of ammo)
  │         │         ├─ Play click/empty sound
  │         │         ├─ (Optional) Auto-reload → Call Reload
  │         │         └─ Return False
```

### 3.6 — Reload Logic

```
Function: Reload
  │
  ├─ CurrentAmmoInMag >= MaxMagazineSize? → Return (already full)
  ├─ Set bIsReloading = true
  │
  ├─ Get Owning Pawn → Get BPC_AmmoComponent
  │    │
  │    └─ Get Ammo Count (AmmoTypeTag = Framework.Item.Ammo.Pistol9mm)
  │         │
  │         ├─ Branch (> 0?)
  │         │    ├─ True:
  │         │    │    ├─ Rounds To Load = Min(AmmoAvailable, MaxMagazineSize - CurrentAmmoInMag)
  │         │    │    ├─ CurrentAmmoInMag += Rounds To Load
  │         │    │    ├─ AmmoComponent → Consume Ammo (RoundsToLoad)
  │         │    │    └─ Play ReloadSound
  │         │    │
  │         │    └─ False: Print "No ammo" → Return
  │         │
  │         ├─ Delay (ReloadTime) → Set bIsReloading = false
  │         └─ Broadcast OnReloadComplete
```

---

## 4. Wire BPC_AmmoComponent (On Player Pawn)

Create BP child of `BPC_AmmoComponent` (spec #70):

| Variable | Type | Default | Purpose |
|----------|------|---------|---------|
| `AmmoPool` | Map (GameplayTag → Integer) | Empty | AmmoType → Count |

```
Function: Get Ammo Count (AmmoTypeTag) → Integer
  │
  └─ AmmoPool.Find(AmmoTypeTag) → Return count (0 if not found)

Function: Consume Ammo (AmmoTypeTag, Amount)
  │
  ├─ Current = Get Ammo Count (AmmoTypeTag)
  ├─ Current -= Amount
  ├─ Clamp to 0 minimum
  └─ AmmoPool[AmmoTypeTag] = Current

Function: Add Ammo (AmmoTypeTag, Amount)
  │
  ├─ AmmoPool.Find or Add (AmmoTypeTag)
  └─ Increment by Amount
```

---

## 5. Wire BPC_RecoilSystem (On Player Pawn)

Create BP child of `BPC_RecoilSystem` (spec #77):

```
Function: Apply Recoil (WeaponData: DA_ItemData*)
  │
  ├─ WeaponData → Get Equipment Data → Get Damage (or FireRate)
  ├─ Calculate kick: PitchUp = random(1.0, 2.5), YawSide = random(-0.5, 0.5)
  │
  ├─ Get Player Controller → Add Controller Pitch Input (PitchUp × -1)
  ├─ Get Player Controller → Add Controller Yaw Input (YawSide)
  │
  └─ (For smooth recovery, use a Timeline or interp to return to original aim)
```

---

## 6. Create DA_Item_Ammo_Pistol9mm (Ammo Data Asset)

```
Content Browser → Game/Items/
  Right-click → Miscellaneous → Data Asset → DA_ItemData
  Name: DA_Item_Ammo_Pistol9mm

Properties:
  ItemTag:   Framework.Item.Ammo.Pistol9mm
  DisplayName: "9mm Rounds"
  ItemType:  Ammo
  StackLimit: 999
  Weight:    0.05
  (AmmoData sub-panel auto-appears — set PerPickupCount = 15)
```

Create `BP_Pickup_Ammo_Pistol9mm` following the same pattern as other pickups. On interact, call `BPC_AmmoComponent.Add Ammo(AmmoTypeTag, PerPickupCount)` instead of adding to inventory.

---

## 7. Verification Checklist

**Step 1 — Pickup:**
- [ ] Pistol pickup in level → mesh visible → interact → goes to inventory
- [ ] Auto-equips to primary weapon slot (weapon appears in player's hand)

**Step 2 — Fire:**
- [ ] Equip pistol, press fire → muzzle flash + sound + hitscan line
- [ ] Hit an enemy actor implementing `I_Damageable` → health decreases
- [ ] Ammo counter decreases (12 → 11 → 10...)
- [ ] Camera kicks slightly on each shot (recoil)
- [ ] Fire rate enforced: cannot spam fire faster than 0.15s

**Step 3 — Empty Magazine:**
- [ ] Fire all 12 rounds → click sound plays, no trace, no damage
- [ ] Auto-reload? Or manual reload prompt appears

**Step 4 — Reload:**
- [ ] Press Reload key → reload animation/sound plays
- [ ] Ammo transfers from pool to magazine
- [ ] Magazine shows 12 rounds again
- [ ] If no ammo in pool → reload fails

**Step 5 — Ammo Pickup:**
- [ ] Pick up `BP_Pickup_Ammo_Pistol9mm` → ammo pool increases by 15
- [ ] Reload → magazine fills, pool decreases by rounds loaded

**Step 6 — Damage Pipeline:**
- [ ] Enemy pawn has `BPC_DamageReceptionSystem` + `BPC_HealthSystem`
- [ ] Fire at enemy → `ApplyDamage(15.0, ...)` called
- [ ] Damage pipeline: resistance → armor → shield → health
- [ ] Enemy health decreases proportionally

---

## 8. Weapon Data Flow (Complete)

```
Player Input (IA_Fire)
  │
  └─ BPC_ActiveItemSystem routes to equipped weapon
       │
       └─ BP_Pistol (implements I_UsableItem)
            │
            ├─ [Ammo Check] → BPC_AmmoComponent (in-mag count)
            ├─ [Hitscan] → Line Trace from MuzzleSocket
            │    │
            │    └─ Hit Actor (implements I_Damageable)
            │         │
            │         └─ BPC_DamageReceptionSystem.ApplyDamage(RawDamage, Causer, Type, Location, Direction)
            │              │
            │              ├─ Calculate Resistance
            │              ├─ Apply Shield (BPC_ShieldDefenseSystem)
            │              ├─ Apply Health (BPC_HealthSystem)
            │              ├─ Evaluate Hit Reaction (BPC_HitReactionSystem)
            │              │    ├─ Stagger? (damage > threshold)
            │              │    └─ Knockdown? (damage > higher threshold)
            │              │
            │              └─ Broadcast OnDamageReceived → UI, audio, metrics
            │
            ├─ [Recoil] → BPC_RecoilSystem.ApplyRecoil()
            ├─ [Effects] → MuzzleFlash, FireSound, ImpactDecal
            └─ [Fire Rate] → Timer lockout until next shot

When magazine empty:
  └─ Player presses IA_Reload
       └─ BP_Pistol.Reload()
            └─ BPC_AmmoComponent.ConsumeAmmo() (from pool)
            └─ BP_Pistol.CurrentAmmoInMag += rounds loaded
```

---

## 9. Key Differences — Weapon vs Tool vs Consumable

| Aspect | Pistol (Weapon) | Flashlight (Tool) | MedKit (Consumable) |
|--------|----------------|-------------------|---------------------|
| ItemType | `Weapon` | `Tool` | `Consumable` |
| Slot | `PrimaryWeapon` | `Tool` | None (inventory bag) |
| Use action | Frame-driven (continuous) | Toggle (single click) | One-shot (consumed) |
| Ammo | Yes (magazine + pool) | No (battery as optional resource) | No |
| Damage output | Yes | No | No (heals) |
| Fire rate | Throttled (timer) | N/A | Use duration (2s anim) |
| Recoil | Yes | No | No |
| Reload | Yes | No | No |
| Stackable | No (StackLimit=1) | No (StackLimit=1) | Yes (StackLimit=5) |
| Interfaces | `I_UsableItem` | `I_UsableItem` + `I_Toggleable` | `I_UsableItem` |
| Pickup → Equip | Auto-equips to slot | Auto-equips to slot | Goes to inventory only |
