Wardogs — GameFeatures/Weapons Reference
Source: Wardogs/Plugins/GameFeatures/Weapons/ (1013 JSON files, 3905 exported objects, 0 parse errors). Parsed with a Python script that splits each file on its --- export: headers, since many files hold several objects. Values are cooked data as exported. Where a meaning is inferred from a property name, the text says so.
Scope: this folder holds the per-item GameFeature bundles: inventory definitions, weapon ballistics, vendor rows, attachment wiring, input bindings, audio and mutables. The
Content/Weaponstree is covered by a separate doc.
Season 02 update (2026-10-15)
- Four new ranged weapons, all with unlock level 1 and bundles under
Platforms/:WEPN_037TCX-SPR (AR, shown price 3200),WEPN_031PP-19 Bizon (SMG, 1000),WEPN_036Evo-3 Ultra (LMG, 7250), andWEPN_034M14 (DMR, 7400). Ranged weapons go from 33 to 37. - Mosin moved. Its item, stats and vendor data now sit under
Platforms/WEPN_017(stats assetDA_WEPN_017_Stats.json). 11 files underSniper/Mosinno longer exist at that path. Its values are unchanged except suppression x, which is now 1.5. - Shown prices, old → new: AK74M 1600 → 800, TAR21 600 → 1200, WEPN_029 2200 → 2000, WEPN_030 6500 → 6000, RFB 6000 → 6250, LMG_02 4500 → 4100, CGM4 2600 → 2400, MMGL 6000 → 7500, RPG7 2000 → 1800, WEPN_027 900 → 750, WEPN_032 800 → 1000, MP9 900 → 1375, Vector 2600 → 5600, WEPN_028 1500 → 1800, SR_04 8800 → 14800, WEPN_035 1100 → 1200.
- Suppression x, old → new: LMG_02 0.45 → 0.9 and SVDM 0.4 → 0.8. New values where the old doc had
-: AK74M 1.05, M4 0.95, M249 1.25, MK22 1.6, SR_04 2.0, SV98 1.5, Vector 0.8, WEPN_035 1.5, Mosin 1.5. - View kick x, old → new: RFB 1.15 → none, SVDM 0.7 → none, WEPN_028 1.1 → 1.15. WEPN_037 is new at 0.9.
- MMGL muzzle speed: 10000 → 7500.
- Attachments and components: 99 → 119 attachment items (Muzzle 40 → 49, Sights 18 → 21, Magazines 28 → 34, Foregrips 13 → 15). Components 74 → 87.
- New vehicle weapon bundles:
VehicleWeaponry/(MWSY_001 mounted heavy machine gun, MWSY_002). These are not in the ranged roster. - Input bindings:
Reload.Discardis now bound on 28 of 37 weapons (was 24). The 14 binding names are unchanged. - Totals: 870 → 1013 JSON files and 3383 → 3905 exported objects.
- Not tabulated here: recoil patterns, aim sway and other curves changed on many weapons. Their values are not in this doc.
Full before/after list: Season 2 Changes → Weapons & Ammo.
1. Folder layout
Each subfolder is one content bundle. A bundle has a GameFeatureData manifest, usually a GameFeatureAction_DataRegistrySource (which registers the vendor and entity-UI tables), and a GameFeatureAction_AddWorldPartitionContent (which adds an External Data Layer). Item definitions live in Data/ID_*.json.
| Group | Files | Item defs | Ranged-weapon exts | Weapon stats assets | GameFeatureData | DataTables |
|---|---|---|---|---|---|---|
| AR | 10 | 0 | 0 | 0 | 6 | 0 |
| SMG | 67 | 5 | 1 | 1 | 5 | 2 |
| LMG | 61 | 5 | 1 | 1 | 2 | 2 |
| DMR | 113 | 8 | 2 | 2 | 2 | 3 |
| Sniper | 130 | 12 | 2 | 2 | 4 | 4 |
| Shotgun | 54 | 5 | 1 | 1 | 2 | 1 |
| Pistols | 51 | 3 | 1 | 0 | 4 | 2 |
| Launchers | 88 | 3 | 2 | 2 | 4 | 2 |
| Platforms | 1134 | 35 | 27 | 26 | 27 | 42 |
| Attachments | 1609 | 119 | 0 | 0 | 141 | 225 |
| Components | 581 | 87 | 0 | 0 | 122 | 0 |
| VehicleWeaponry | 7 | 1 | 0 | 1 | 2 | 0 |
Notes on the layout:
AR/AR_01toAR_07(six folders, 10 files) hold only GameFeature shells: 6GameFeatureDataand 4 world-partition actions. The AR-class item definitions are not in this folder.Platforms/WEPN_0xx(27 folders) holds the full bundles for the newer weapons. The older hand-named bundles sit inDMR,Sniper,LMG,Shotgun,SMG,PistolsandLaunchers(RFB,SVDM,MK22,Mosin,SR_04,MP43,SMG_03,LMG_02,Pistol_02,Launcher_04,RPG7). The Mosin item and stats data now also sit underPlatforms/WEPN_017.Attachments/holds 119 attachment items (Foregrips 15, Magazines 34, Muzzle 49, Sights 21), of which 113 have vendor rows.Components/holds 87 weapon-specific items (Barrels 25, Handguards 20, Pistol Grips 15, Stocks 21, Triggers 3, Dust Covers 3), none of which have vendor rows.
Object types (3905 exported objects):
| Type | Count | What it holds |
|---|---|---|
| WDItemInputBehavior_ActivateAction | 435 | Input action bindings (payload is empty in this export) |
| GameFeatureData | 321 | Bundle manifest; scans BH.Item assets under /<Bundle>/Data |
| GameFeatureAction_AddWorldPartitionContent | 303 | Adds an External Data Layer (EDL_*) |
| BHInventoryItemDefinition | 283 | Item root: ItemId, display name, ItemContext tags, DataFragments |
| DataTable | 283 | Vendor rows (WDVendorData) and entity UI rows (BHEntityData_UI) |
| WDInventoryFragment_Gunsmith | 271 | Gunsmith item type (Attachment, Weapon, etc.) |
| WDInventoryFragment_Attachment | 245 | Attachment type tag, applied GameplayEffects, removal flags |
| WDInventoryFragment_Audio | 221 | Pointer to a WDAudioData asset |
| BHInventoryFragment_CustomShape | 165 | Inventory shape fragment (no properties exported) |
| BHInventoryFragment_Droppable | 165 | Dropped-world mesh reference |
| BHInventoryFragment_UserInterface | 165 | UI fragment (no properties exported) |
| GameFeatureAction_DataRegistrySource | 160 | Registers DataTables into the VendorData / EntityData_UI registries |
| BHInventoryFragment_Weight | 162 | Item weight (units not stated) |
| BHInventoryFragment_SubSlotSupport | 119 | Sub-slot containers (what can be fitted or loaded) |
| WDItemInputBehavior_CompleteAction | 74 | Release-side input actions |
| BHAnimationSet | 45 | Animation set reference |
| B_DefaultWeaponInputData_C | 37 | Per-weapon input table (14 bindings) |
| BHItemExtensionData_AimAssistExtension | 37 | Aim-assist settings and filter assets |
| WDInventoryFragment_ItemActor | 37 | Container for the extension-data fragments |
| WDItemExtensionData_Animation | 37 | Animation set reference |
| WDItemExtensionData_AudioExtension | 37 | Audio asset reference |
| WDItemExtensionData_GameplayEffects | 37 | Default weapon attributes (Attribute.Gameplay.Weapon.*) |
| WDItemExtensionData_Holdable | 37 | Equip/holster montages, hip offset, FOV when ADS |
| WDItemExtensionData_Inventory | 37 | Ammo requirement for primary fire |
| WDItemExtensionData_Mutable | 37 | Customization parameters on MUT_MasterWeapon |
| WDItemExtensionData_RangedWeapon | 37 | Stats link, spread, stance effects, ADS transition, damage tag |
| WDAudioData | 38 | Sound list per weapon (and one for the vehicle machine gun) |
| WDItemsmithDataFragment_CameraData | 36 | Gunsmith camera framing |
| WDWeaponStatsData | 36 | Ballistics, recoil, aim sway, ADS curves (DA_*_Stats) |
| CurveFloat | 7 | Standalone float curves (not decoded here) |
| WDInventoryFragment_TransferTime | 1 | Item fragment, present once (on Launcher_04); not decoded here |
2. Weapon roster (37 ranged weapons)
Each weapon is found through its WDItemExtensionData_RangedWeapon, which names its WDWeaponStatsData asset. Price and unlock level come from the vendor variation with bShowInVendor = true. “Class” is the first segment of the damage tag (Meta.Damage.Weapon.<Class>.*). It is the most consistent family label in the data.
| ID | Display name | Class | Archetype tag | Shown price | Unlock lvl | Primary-fire ammo (type) | Stats asset |
|---|---|---|---|---|---|---|---|
| A91 | A-91 | AR | AR | 0 | 1 | 556mm (Magazine) | DA_A91_Stats.json |
| AK74M | AK74 | AR | AR | 800 | 1 | 545mm (Magazine) | DA_AK74M_Stats.json |
| KH2002 | KH-2002 | AR | AR | 0 | 1 | 556mm (Magazine) | DA_KH2002_Stats.json |
| M4 | M4 | AR | AR | 2800 | 1 | 556mm (Magazine) | DA_M4_Stats.json |
| TAR21 | T-21 | AR | AR | 1200 | 1 | 556mm (Magazine) | DA_TAR21_Stats.json |
| WEPN_029 | Galil | AR | AR | 2000 | 1 | 556mm (Magazine) | DA_WEPN_029_Stats.json |
| WEPN_030 | FAL | AR | AR | 6000 | 1 | 308Win (Magazine) | DA_WEPN_030_Stats.json |
| WEPN_033 | Bushmaster M17S | AR | AR | 0 | 1 | 556mm (Magazine) | DA_WEPN_033_Stats.json (copy, see 8.1) |
| WEPN_037 | TCX-SPR | AR | DMR | 3200 | 1 | 762mm (Magazine) | DA_WEPN_037_Stats.json |
| RFB | BMR-308 | DMR | DMR | 6250 | 1 | 308Win (Magazine) | DA_RFB_Stats.json |
| SKS | SKS | DMR | DMR | 2400 | 1 | 762mm (Ammo) | MISSING (no stats asset in folder) |
| SVDM | SVD | DMR | DMR | 4800 | 1 | 762x54mm (Magazine) | DA_SVDM_Stats.json |
| WEPN_034 | M14 | DMR | DMR | 7400 | 1 | 308Win (Magazine) | DA_WEPN_034_Stats.json |
| LMG_02 | PKM | LMG | LMG | 4100 | 1 | 762x54mm (Magazine) | DA_LMG_02_Stats.json |
| M249 | M249 SAW | LMG | LMG | 3200 | 1 | 556mm (Magazine) | DA_M249_Stats.json |
| WEPN_036 | Evo-3 Ultra | LMG | LMG | 7250 | 1 | 308Win (Magazine) | DA_WEPN_036_Stats.json |
| CGM4 | MAAWS | Launcher | Launcher | 2400 | 1 | 84mm (Ammo) | DA_CGM4_Stats.json |
| Launcher_04 | 9K333 Verba | Launcher | Launcher | 800 | 1 | 72mm (Ammo) | DA_Launcher_04_Stats.json |
| MMGL | MGL-40 | Launcher | Launcher | 7500 | 1 | 40mm (Ammo) | DA_MMGL_Stats.json |
| RPG7 | RPG-7 | Launcher | Launcher | 1800 | 1 | 93mm (Ammo) | DA_RPG7_Stats.json |
| Glock17 | GGX 17 | Pistol | Pistol | 200 | 1 | 9mm (Magazine) | DA_Glock17_Stats.json |
| Judge | Judge | Pistol | Pistol | 250 | 1 | 45Colt (Ammo) | MISSING (no stats asset in folder) |
| WEPN_026 | M1911 | Pistol | Pistol | 300 | 1 | 45ACP (Magazine) | DA_WEPN_026_Stats.json |
| WEPN_027 | Deagle | Pistol | Pistol | 750 | 1 | 50AE (Magazine) | DA_WEPN_027_Stats.json |
| WEPN_032 | GGX 18 | Pistol | Pistol | 1000 | 1 | 9mm (Magazine) | DA_WEPN_032_Stats.json |
| MP9 | AMP-9 | SMG | SMG | 1375 | 1 | 9mm (Magazine) | DA_MP9_Stats.json |
| SMG_03 | PP-19 Vityaz | SMG | SMG | 1200 | 1 | 9mm (Magazine) | DA_SMG_03_Stats.json |
| Vector | Super-45 | SMG | SMG | 5600 | 1 | 45ACP (Magazine) | DA_Vector_Stats.json |
| WEPN_028 | MP5 | SMG | SMG | 1800 | 1 | 9mm (Magazine) | DA_WEPN_028_Stats.json |
| WEPN_031 | PP-19 Bizon | SMG | SMG | 1000 | 1 | 9mm (Magazine) | DA_WEPN_031_Stats.json |
| M500 | M500 | Shotgun | Shotgun | 1200 | 1 | 12g (Ammo) | DA_M12G_Stats.json |
| MP43 | MP43 | Shotgun | Shotgun | 400 | 1 | 12g (Ammo) | DA_MP43_Stats.json |
| MK22 | MK22 | Sniper | Sniper | 6400 | 0 | 308Win (Magazine) | DA_MK22_Stats.json |
| Mosin | Mosin Nagant | Sniper | Sniper | 4500 | 1 | 762x54mm (Ammo) | DA_WEPN_017_Stats.json |
| SR_04 | AMR 50 | Sniper | Sniper | 14800 | 1 | 50Cal (Magazine) | DA_SR_04_Stats.json |
| SV98 | SV98 | Sniper | Sniper | 5200 | 1 | 762x54mm (Magazine) | DA_SV98_Stats.json |
| WEPN_035 | Scout Rifle TD | Sniper | Sniper | 1200 | 1 | 556mm (Ammo) | DA_WEPN_035_Stats.json |
A shown price of 0 is reported as-is for KH2002, A91 and WEPN_033 (see 8.3).
Per-class summary
| Class | Weapons | IDs | RPM | Muzzle speed | Eff. range | Shown price (non-zero) |
|---|---|---|---|---|---|---|
| AR | 9 | AK74M, M4, KH2002, A91, TAR21, WEPN_029, WEPN_030, WEPN_033, WEPN_037 | 650-800 | 70000-95000 | 300-800 | 800-6000 |
| SMG | 5 | Vector, MP9, WEPN_028, SMG_03, WEPN_031 | 700-1200 | 37500-40000 | 50-200 | 1000-5600 |
| LMG | 3 | LMG_02, M249, WEPN_036 | 700-850 | 79000-91500 | 700-1000 | 3200-7250 |
| DMR | 4 | RFB, SVDM, SKS, WEPN_034 | 450-700 | 80000-88000 | 500-800 | 2400-7400 |
| Sniper | 5 | SV98, WEPN_035, MK22, Mosin, SR_04 | 41.5-46 | 83000-97500 | 300-2000 | 1200-14800 |
| Shotgun | 2 | M500, MP43 | 120-900 | 39100-48000 | 70 | 400-1200 |
| Pistol | 5 | Judge, Glock17, WEPN_026, WEPN_027, WEPN_032 | 267-1200 | 25300-47000 | 50 | 200-1000 |
| Launcher | 4 | Launcher_04, RPG7, MMGL, CGM4 | 75-100 | 2000-23000 | 300-1000 | 800-7500 |
Values are the raw numbers from the stats assets. No unit conversion has been applied.
3. Per-weapon ballistics (from the linked WDWeaponStatsData)
Column meanings are inferred from property names. FocusEnergyReductionPerShot and RecoilReductionWhenFocussing are read as a focus-energy cost and recoil relief. WeaponAccuracy.AccuracyValue is the full angle of the accuracy cone (milliradians by default, minutes of angle when AccuracyType says so; lower is tighter), confirmed against code on Weapon Spread. The other columns are still inferred from property names.
| ID | RPM | Muzzle spd | Eff. range | Accuracy | Deviation (deg) | Pitch recoil max | Focus / shot | Recoil relief | View kick x | ADS stab. (s) | Suppression x | Burst | Melee |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| A91 | 700 | 71500 | 300 | 1.5 | 15 | 1* | 0* | 0* | 1* | 0.9 | 1.3 | 3* | 35 |
| AK74M | 650 | 88000 | 500 | 0.825 | 13 | 45 | 25 | 20 | 1.1 | 1.2 | 1.05 | 3* | 35 |
| KH2002 | 700 | 71500 | 300 | 1.5 | 15 | 45 | 40 | 30 | 1* | 1.1 | 1.3 | 3* | 35 |
| M4 | 800 | 91000 | 500 | 0.84 | 10 | 45 | 25 | 20 | 1.08 | 1 | 0.95 | 3* | 35 |
| TAR21 | 750 | 91000 | 550 | 0.87 | 15 | 45 | 40 | 30 | 1.1 | 1 | 1* | 3* | 35 |
| WEPN_029 | 650 | 95000 | 400 | 0.73 | 12 | 45 | 25 | 20 | 0.95 | 1.25 | 1* | 3* | 35 |
| WEPN_030 | 650 | 84000 | 800 | 0.58 | 16 | 45 | 25 | 20 | 1* | 1.35 | 0.25 | 3* | 35 |
| WEPN_033 | 700 | 71500 | 300 | 1.5 | 15 | 45 | 40 | 30 | 1* | 1.05 | 1.3 | 3* | 35 |
| WEPN_037 | 800 | 70000 | 400 | 0.58 | 14 | 45 | 50 | 30 | 0.9 | 1 | 0.4 | 1 | 35 |
| RFB | 450 | 88000 | 500 | 0.69 | 10 | 45 | 50 | 30 | 1* | 2 | 0.6 | 1 | 35 |
| SKS | 420 | 73500 | 400 | 0.73 | 5.5 | 45 | 50 | 30 | 1* | 2 | 1.1 | 3* | 35 |
| SVDM | 480 | 80000 | 800 | 0.45 | 10 | 45 | 50 | 30 | 1* | 1.75 | 0.8 | 3* | 35 |
| WEPN_034 | 700 | 85300 | 500 | 0.73 | 10 | 45 | 50 | 30 | 1* | 2 | 0.5 | 1 | 35 |
| LMG_02 | 600* | 82500 | 1000 | 2.5 | 20 | 45 | 10 | 50 | 1.25 | 3.5 | 0.9 | 3* | 40 |
| M249 | 850 | 91500 | 700 | 4.3 | 20 | 45 | 10 | 50 | 1.2 | 3 | 1.25 | 3* | 40 |
| WEPN_036 | 700 | 79000 | 1000 | 0.73 | 15 | 45 | 10 | 50 | 1* | 4 | 0.6 | 3* | 40 |
| CGM4 | 100 | 23000 | 400 | 2.5 | 5* | 1* | 0* | 0* | 1* | 1.75 | 1* | 3* | 40 |
| Launcher_04 | 100 | 2000 | 1000 | 1* | 5* | 1* | 0* | 0* | 1* | 2 | 1* | 3* | 40 |
| MMGL | 75 | 7500 | 400 | 20 | 5* | 45 | 0* | 0* | 1* | 1.5 | 1* | 3* | 40 |
| RPG7 | 100 | 10000 | 300 | 3 | 5* | 1* | 0* | 0* | 1* | 1.5 | 1* | 3* | 40 |
| Glock17 | 600* | 37500 | 50 | 3.49 | 10 | 1* | 40 | 10 | 1.5 | 0.6 | 1* | 3* | 20 |
| Judge | 200 | 33500 | 50 | 3.49 | 10 | 45 | 40 | 10 | 1* | 1.5 | 1* | 3* | 20 |
| WEPN_026 | 430 | 25300 | 50 | 2 | 10 | 45 | 40 | 10 | 1.85 | 0.75 | 1* | 3* | 20 |
| WEPN_027 | 267 | 47000 | 100* | 0.84 | 5* | 45 | 40 | 10 | 1* | 0.9 | 1* | 3* | 20 |
| WEPN_032 | 1200 | 37500 | 50 | 2 | 10 | 45 | 40 | 10 | 1* | 0.55 | 1* | 3* | 20 |
| MP9 | 900 | 40000 | 100* | 2.1 | 15 | 1* | 25 | 15 | 1.1 | 0.6 | 1* | 3* | 20 |
| SMG_03 | 800 | 38000 | 200 | 1.3 | 17 | 45 | 50 | 30 | 1* | 0.8 | 1* | 3* | 20 |
| Vector | 1200 | 37500 | 50 | 2 | 35 | 45 | 25 | 15 | 1* | 0.65 | 0.8 | 3* | 20 |
| WEPN_028 | 800 | 40000 | 200 | 1.16 | 20 | 45 | 25 | 15 | 1.15 | 0.7 | 1* | 3* | 20 |
| WEPN_031 | 700 | 38000 | 200 | 1.3 | 17 | 45 | 50 | 30 | 1* | 0.8 | 1* | 3* | 20 |
| M500 | 120 | 48000 | 70 | 1.5 | 24 | 45 | 0* | 0* | 1.6 | 1.1 | 1* | 3* | 35 |
| MP43 | 900 | 39100 | 100* | 3.08 | 28 | 45 | 0* | 0* | 1.15 | 1 | 1* | 3* | 35 |
| MK22 | 46 | 89400 | 1000 | 0.16 | 4 | 45 | 100 | 0* | 1.4 | 0.7 | 1.6 | 3* | 40 |
| Mosin | 46 | 86500 | 500 | 0.78 | 4 | 45 | 100 | 0* | 0.8 | 0.7 | 1.5 | 3* | 40 |
| SR_04 | 41.5 | 90000 | 2000 | 0.11 | 4 | 45 | 100 | 0* | 1.45 | 0.9 | 2.0 | 3* | 40 |
| SV98 | 46 | 83000 | 1000 | 0.56 | 4 | 45 | 100 | 0* | 1.3 | 0.8 | 1.5 | 3* | 40 |
| WEPN_035 | 46 | 97500 | 300 | 1.5 | 4 | 45 | 100 | 0* | 0.3 | 0.55 | 1.5 | 3* | 40 |
* marks a value the asset omits. An omitted property takes the class default, so these cells show the constructor value, not “no value” (Data for the omission, Native for the value; the - that remained are cells for properties this table does not read). SKS now shows its own values; the old row was all - although DA_SKS_Stats carries them.
Native: class defaults of WDWeaponStatsData. The export drops every property equal to the class default, so DA_LMG_02_Stats (PKM) has no RoundsPerMinute key and uses the default. UWDWeaponStatsData is 0x11D0 bytes with a WDWeaponStats struct (0xE48 bytes) at +0x38. The class constructor wrapper at RVA 0x6BBCDF0 sets the vtable (0xB32BAB0), copies a global tag (RVA 0xD2692F8, not decoded) into WeaponId at +0x30, calls the struct constructor at RVA 0x699E6C0 for WeaponStats, then the BackBlast constructor at +0xE80. The struct constructor writes RoundsPerMinute (+0x138) = 600.0 (dword 0x44160000) and these other defaults (struct offsets in brackets): InitialBulletSpeed 100000 [0xDA0], EffectiveRange 100 [0xDA4], WeaponAccuracy.AccuracyValue 1.0 [0x4], RandomDeviationDegree 5.0 [0xCE0], RecoilPatternPitchMax 1.0 [0xCB8], FocusEnergyReductionPerShot 0 [0x94], RecoilReductionWhenFocussing 0 [0x9C], ViewKickMagnitudeMultiplier 1.0 [0xB78], AdsStabilizationData.AdsStabilizeTime 0.5 [0xAC0], SuppressionMultiplier 1.0 [0xDA8], RoundsPerBurst 3 [0x150], MeleeDamage 25 [0xDAC], Min/Max/ZeroingRange 100 and ZeroingStep 100 [0x160-0x168]; SupportedFireModes is an empty array [0x140]. The exported default of the Blueprint copy of the struct (B_Action_WeaponFire.Weapon Stats, round-8 export, not published) independently shows RoundsPerMinute 600.0, RoundsPerBurst 3, IronSightsAimTime 0.2 and zeroing 100/100/100. Reproduce the cell fill with python _tools/weapon_stats_defaults.py --check.
Rate of fire (consumer found). Native: UWDWeaponComponent keeps its own copy of the 0xE48-byte stats struct at component +0x140 (0x1D2C340: lea rax, [rcx+0x140]), so RoundsPerMinute is read at component +0x278. The one-line getter 0x5F7ED10 returns that float (*(float*)(a1+0x138)). The start-fire routine 0x6B980A0 (UWDWeaponComponent vtable slot 175) calls it with component+0x140 and computes the shot interval as interval = 59.999996 / RoundsPerMinute seconds (constant 0x426FFFFF at RVA 0xB2D3C08, 60.0 minus one float step; the call is at 0x6B981C1, the divss at 0x6B981D9). It takes the world time (double at +0x840 of the world object) minus the last-shot time (float at component +0xFE8) as the time since the last shot. If that is outside (0, interval) (or no shot yet) it fires at once through 0x6B94700 (the body of Timer_FireWeapon, exec thunk 0x6B99AB0) and, in the fire modes held as 1 or 2 at +0xF88, starts a looping FTimerManager::SetTimer (0x143AB8810, handle at +0x10F0) with rate = interval; if the remaining wait is 0.1 s or less it sets a one-shot timer for the remainder and then the loop; if more than 0.1 s it does nothing yet. The timer delegate 0x6B98950 calls the same 0x6B94700. Derived: the PKM at 600 RPM therefore fires every 0.1 s (0.099999994 s). Other readers of the getter: AI shoot task 0x887AD30 (60.0 / RPM), AI weapon-data field 6 RoundsPerMinute (0x6A73020, enum EAIWeaponDataField), 0x867A3A0, 0x867A550, 0x6F1C310 (Itemsmith stats query, display only; decoded in round 27 below), 0x6AB46A0 (classified in round 26 as audio). The Blueprint B_Action_WeaponFire plays no part in the cadence: it copies the struct into its Weapon Stats variable (@4919) but never reads RoundsPerMinute, and its FireRateMs variable (+0x164) has no reader in the listing. A second copy, FWDWeaponStatsQueryData.RoundsPerMinute (+0x28), feeds the Itemsmith FireRateText UI only. Not found: no Attribute.Gameplay.Weapon.* name concerns fire rate (the 14 names in data/ and the attribute doc are AimTime, recoil, spread, accuracy, equip/holster speed, zoom, zeroing, length, detonator range, focus energy), no GameplayEffect in data/ touches LMG_02, and the getter returns the raw float, so nothing was found that scales the 600. Search done: capstone scan of the whole .text for float loads from [reg+0x138], [reg+0x170], [reg+0x270], [reg+0x3D0] within 160 bytes of a 60.0, 1/60, 60000 or 1000 constant (negative), then the weapon-component range 0x6B80000-0x6BC0000 for those constants (positive: 0x6B981C6).
Rate of fire: how the component’s copy is filled, and whether anything rewrites it (round 18). Evidence: static-analysis image only, not devirtualized; decompilation and disassembly are not equivalence proof, and nothing was run. Native: the data chain is item ID_* -> WDItemExtensionData_RangedWeapon.WeaponStats (data asset +0x98, the WDWeaponStatsData asset) -> UWDWeaponComponent.InitWeapon(const UWDWeaponStatsData*) -> the component’s embedded stats struct at +0x140 -> the start-fire routine. Details:
UWDWeaponComponenthas two stats-asset pointers:DefaultWeaponStatsDataat+0x128(SDK, an editable class default) andActiveWeaponStatsDataat+0x130.InitWeaponisUWDWeaponComponentvtable slot 171 (vtable slot 175 is start-fire; the slot pointer0xB3155E0holds RVA0x6B96720). It stores the asset in+0x130, takes the asset’s stats struct with the one-line getter0x17E78D0(lea rax,[rcx+0x38]), copy-assigns it over the component’s struct at+0x140through0x6B8F5A0(the struct copy visitsRoundsPerMinuteat struct+0x138, so component+0x278), and copies the firstSupportedFireModesentry into the fire-mode byte at+0xF88.- Callers that pass a stats asset: the ranged-weapon item extension (
0x6C99700-0x6C9976E: checks the loaded data’s class, then callsWeaponComponent(+0x128 of the extension)->vtable[0x558](data +0x98)), the component’s own default path (0x6B902E0-0x6B90315: passesDefaultWeaponStatsData+0x128), and the vehicle weapon extension (0x7647B5D-0x7647B75, extension data+0x80). The vtable-offset-0x558 call exists in about 60 places because other classes share the offset; only these three were matched to a stats asset. The remaining sites were not individually classified (open). - A component that never receives an asset keeps the constructor value: the component constructor (
0x6B8E5A0) calls the struct constructor0x699E6C0on+0x140(call at0x6B8E65B), so RPM is 600 untilInitWeaponruns. - No rewrite found.
RoundsPerMinuteis read only through the getter0x5F7ED10(directcall rel32sites found by byte scan:0x6A73073,0x6AB4BAF,0x6B981C1,0x6F1C57B,0x6F1C616,0x867A44A,0x867A5F6,0x887B15D; only0x6B981C1is in the weapon component; indirect calls are not covered). In the weapon-component code (0x6B80000-0x6BC0000) nothing stores to component+0x278; no instruction that writes the+0x140struct was found other than the struct copy routines (0x6B8F5A0, called byInitWeapon, and0x6B8416D, which round 20 found copies a different struct). The only component-level “modifier” field isInitialBulletSpeedModifier(+0x114, bullet speed, set bySetInitialBulletSpeedModifier). No attribute name or gameplay effect in the data concerns fire rate (see above), and fire-mode code was not seen touching the float (CycleFireModewas read in round 20: it stores only the fire-mode byte, see below). So item data, via the asset, is the only source of the interval, and the 600 default applies whenever the asset omitsRoundsPerMinute. - Not verified (open): (b) per-site classification of the other
vtable[0x558]calls outside the weapon, item-extension and vehicle ranges (round 20 classified the ones inside them, below); (d) runtime confirmation, since the image was never executed. Items (a), (c) and theCycleFireModequestion were worked in round 20 (next paragraph). - Virtualization check. The protection layer’s dispatch stubs (the
mov reg,[rip+x]; jmp [reg+disp]pattern) were scanned with the project classifier over the bodies of start-fire (0x6B980A0-0x6B983F7),InitWeapon(0x6B96717-0x6B96812), the getter0x5F7ED10, the extension routine (0x6C99700-0x6C9977F), the component constructor (0x6B8E5A0-0x6B8E8CD) and the struct constructor (0x699E6C0-0x699EA00): no stub inside any of them, and every call in the start-fire body is a directrel32call or a documented vtable call. The weapon-component range does contain 31 stubs elsewhere (for example the thunk at0x6B99300), so the result is “none in the functions read”, not “none in the binary”. Because the image is a static-analysis image and the layer’s virtualized dispatch is not removed, a negative search outside these bodies stays inconclusive. This is why the “no modifier” result above is bounded to the scanned range and the call sites listed.
Rate of fire: equip/spawn trigger, struct copies, CycleFireMode (round 20). Evidence: static-analysis image; virtualized dispatch not removed. Disassembly only, nothing was run. Pages that differ between the two images were read from the older reference image: the exec-thunk page 0x6B99000, page 0x6BBB000 and page 0x675C000 (helpers 0x675CDB0, 0x675CE10, 0x675CEB0). The thunk 0x6B99270 was compared byte for byte in both images. Every other function cited here sits on a page that is byte-identical in both images.
- (a) What triggers
0x6C99700. Native:0x6C99700is the body ofUWDItemExtension_RangedWeapon::OnRep_WeaponComponent. The SDK declaresWeaponComponentat extension+0x128asNet, RepNotify, Transient, and the class’s native-function table pairs the nameOnRep_WeaponComponent(0xB389E80) with the exec thunk0x6CA1660(slot at0xB389E88), whose tailjmpat0x6CA1675goes to0x6C99700. Two ways reach it. (1) The engine calls the OnRep on a client when the pointer replicates; the dispatch itself is engine code and was not read. (2) The extension’s virtual slot 159 (0x6C9AFB0, vtable0xB389548, slot pointer0xB389A40) calls it directly at0x6C9B59E, with no role test around the call, after installing the weapon’s default attributes. These are the only two references to0x6C99700in the image (byte scan forcall/jmp rel32and for the absolute pointer). The server sets the pointer in the extension’sOnRegister(slot 110,0x6C99FA0, slot pointer0xB3898B8): behind the owner role test0x675CE10([owner+0x170] == 3, i.e. authority) it finds the weapon component, stores it at+0x128and calls0x2480AB0(this, [this+0x38], 3)(not decoded; read as a replication-dirty mark, not verified). - Who calls slot 159. The item component’s routine
0x6C561C0walks the list of newly added extensions (+0x378bit 4 gates which) and calls vtable+0x518(slot 163) then vtable+0x4F8(slot 159) on each, at0x6C56658and0x6C56664.0x6C561C0is called from three virtual entries:0x6C56730(call at0x6C5673B, argument 0),0x6C56C00(call at0x6C56C30, argument 0; it also sets+0x2CCand flag bit 4) and0x6C56E30(jump at0x6C56E51, argument 1, only when+0x378bit 1 is set, a deferred retry). The bit-1 flag is cleared at0x6C56390and set at0x6C5640D. The class that owns these three entries, their slot numbers and the engine events that run them are open. Derived: InitWeapon therefore runs on whichever machine builds or receives the item’s extension list, so the server and each client fill their own stats copy from the same item asset. The component’sActiveWeaponStatsData(+0x130) carries noNetflag in the SDK and the+0x140block is unreflected padding there, so the stats struct is not replicated as a property. - Is InitWeapon re-run on attachment or fire-mode change? No static path found. The image has no
call rel32to InitWeapon (0x6B96720is referenced only by the vtable slot0xB3155E0). Of the 62call [reg+0x558]sites in the image, those inside the weapon, item-extension and vehicle ranges are:0x6C9976E(the OnRep above),0x6B90315(component default path),0x7647B75(vehicle extension), and0x6B992E3, which is the exec thunk0x6B99270of the Blueprint-callableInitWeaponitself (name at0xB315820, pointer0xB315828; it forwards the asset pointer from the script frame). No exported Blueprint listing indata/callsInitWeapon. The remaining sites in those ranges have other shapes and are not InitWeapon:0x6B7A765(takes a bool),0x6CAB38Band0x6CABD17(receiver at+0x2F0, result read back) and0x763024A(result read back from a stack buffer). The attachment paths (OnRep_ReplicatedAttachmentMap, thunk0x6CA1680;SetPredictedAttachment;0x6C9D170) are not among the0x558sites, so attachments do not go through InitWeapon; they act through the attribute route described in the Gameplay Effects pipeline. - (c) The struct copy constructor
0x6B8EFF0is not the weapon stats struct. It copies a different, smaller struct: the highest offset it copies is about+0x12A, and the array-copy loop that calls it at0x76DB37Estrides0x320bytes, while the stats struct strides0xE48(loop at0x6BBB7FE). It does not touch+0x138, the RPM slot of the stats struct. Callers:0x6B8C782(function0x6B8C720: builds a stack copy atrsp+0x390fromrsp+0x50, assigns it into the object at[this->[0]+0xE0]with0x1418FCDB0, then destroys it with0x6B8ED40),0x76C1A08(same shape: temp atrbp+0x2B0assigned to[obj+0x30]),0x76D4CABand0x76D4CDC(function0x76D4B40: a copy of a looked-up element into a stack temp that is only tested with0x1417FC060) and0x76DB37E(array copy loop, stride0x320). None of them reads or writes the weapon component at+0x140, so none is a per-attachment copy of the stats struct. Correction to the list above: the “sibling at0x6B8416D” copies yet another struct (byte at+0, qword at+4, nested copy from+0x10, fields near+0x90); it does not access+0x138or+0x278in its first0x290bytes. The routines that copy the real 0xE48 stats struct (0x6B8F5A0) are:InitWeapon(0x6B9676E, destination is component+0x140), the exec thunk0x6B99160ofGetWeaponStats(name at0xB3157E0; destination is the Blueprint return buffer and the source is component+0x140, so it only reads the copy), and the array element loop at0x6BBB7FE(function0x6BBB7D0, copies between arrays of stats structs, not into the component).InitWeaponremains the only writer of the component’s copy that was found. - (3)
CycleFireModedoes not touch the RPM.UWDWeaponComponent::CycleFireMode(bool)is native at0x6B91330(exec thunk0x6B98EF0, native-table slot0xB315750).Server_CycleFireModehas its thunk at0x69E0C40, which dispatches through vtable+0x528(slot 165, slot target0x6B97810), and that target isCycleFireMode(this, true). The body readsSupportedFireModesthrough0x1D2C340(component+0x140); because the accessor islea rax,[rcx+0x140], it returns the stats struct’s+0x140TArray, so0x1D2C340is a struct-relative accessor and is not itself a witness for the component+0x140offset. It finds the current mode byte+0xF88in the array, takes the next entry with wraparound and stores it at0x6B914B0(mov [rbx+0xF88], r8b). That is the only store through the component pointer in the function (instruction scan of0x6B91330-0x6B91700). It then calls vtable+0xF8and+0x60on the owner object ([component+0x20]) and builds an event carrying the new mode byte. Start-fire reads+0xF88only to choose the timer form; the interval comes from the RPM alone, so a fire-mode change alters whether a looping timer is used (modes 1 and 2), not the RPM value. The indirect callees were not decoded, so a write to+0x140from them cannot be excluded (open). - Open after round 20. (i) The class, slot numbers and engine events of the three entries that call
0x6C561C0; (ii) the meaning of0x2480AB0(this, [this+0x38], 3)inOnRegister; (iii) the remainingvtable[0x558]sites outside the weapon, item-extension and vehicle ranges; (iv) indirect callees ofCycleFireMode; (v) no runtime check was made. Virtualized dispatch is not removed in the analysis image, so every negative above is bounded to the functions and ranges named.
Rate of fire: second-consumer search (round 26): no further consumer found. Evidence: static-analysis image only, virtualized dispatch not removed; disassembly and corpus call tables, nothing was run. Question: does anything besides the start-fire routine 0x6B980A0 read the RPM or re-arm the shot timer, including burst, semi and stance logic?
- Timer re-arm sites (confirmed for direct calls). A byte scan of the whole
.textforcall/jmp rel32toFTimerManager::SetTimer(0x3AB8810) found 104 sites. Only four have a weapon-component timer handle (+0x10F0,+0x10F8or+0x1100) within the 0x300 bytes before the call:0x6B9826C,0x6B982CAand0x6B9839B(all inside start-fire, the loop timer and the one-shot remainder timer already described) and0x6B94411(inside0x6B93610, the post-shot routine called from the fire body0x6B94700). The fourth is not a cadence timer: its rate isRecoilPatternResetTime(stats struct+0xCE4, read by the one-line getter0x6BBD5B0, SDKFWDWeaponStats), its handle is+0x1100, and its delegate0x6B97550only stores -1 into component+0xFF0(the recoil pattern index reset). So the shot interval is armed in exactly one place. - Stop / re-arm. Vtable slot 176 (
0x6B98560, slot pointer0xB315608) is the stop-fire routine: it zeroes the burst counter (+0xFEC), clears the timer handles at+0x10F0and+0x10F8, and sends the stop event. It never writes a rate. The timer delegate0x6B98950only calls0x6B94700. (static-only: the decompiler output was read, not equivalence-proved.) - Burst logic (confirmed). Burst is fire-mode byte
+0xF88== 2. In0x6B93610(0x6B93E74-0x6B93E9E) each shot doesinc byte [comp+0xFEC]; when it equalsRoundsPerBurst(the getter0x64D1800:movzx eax, byte [rcx+0x150], stats struct+0x150) the counter is reset and vtable+0x580(slot 176, stop-fire) is called. Burst therefore ends the loop by stopping it; the interval inside a burst is the same59.999996 / RPM. Semi (mode 1) and full-auto differ only in the timer form chosen in start-fire and in post-shot feedback (0x6B96160, which reads+0xF88/+0xFECfor sound and animation events). None of these touchesRoundsPerMinute. - Second readers of the RPM field (confirmed negative in the scanned set). (1) The only direct callers of the getter
0x5F7ED10(movss xmm0,[rcx+0x138]; ret, so it is a plain struct read) remain the eight sites listed in round 18 (re-verified from the corpus call table plus acall/jmp rel32byte scan; no tail-jump callers). (2) A byte scan of the whole.textfor scalar-float loads and compares (movss,mulss,divss,cvtss2sd,comiss, …) with displacement+0x138(98 hits) or component-level+0x278(101 hits) was filtered for weapon-component fingerprints (displacements+0xF88,+0xFE8,+0xFEC,+0x10F0within 0x600 bytes): no hit has one, and none lies in the weapon-component range0x6B80000-0x6BC0000except the unrelated0x6B82A27(a vitality-component slot). A capstone pass over0x6B80000-0x6BC0000for memory operands at+0x278,+0x10F0,+0x10F8,+0x1100,+0xFEC,+0xF88,+0xFE8found no float access to+0x278(its nine+0x278hits are 8-byte or integer accesses in other classes’ functions such as0x6B8D830,0x6B8E100,0x6B8ED40,0x6B8EFF0,0x6B9C910) and no store to the RPM slot outside the struct copy inInitWeapon. - The other getter callers, now classified (static-only).
0x867A3A0and0x867A550are in the automation orchestrator component (UWDAutomationOrchestratorComponent, caller0x867A0B0), a test-automation path, not live firing.0x6AB46A0(vtable slot 91 ofUWDAudioEventBehaviorKillingBlow) reads the RPM at0x6AB4BAFand passes it to an audio-event call: an audio parameter, not a cadence.0x6F1C310(calls at0x6F1C57B,0x6F1C616) is the Itemsmith weapon-stats query builder: it fills anFWDWeaponStatsQueryData(Damage+0x08,Range+0x0C,Accuracy+0x10,Recoil+0x18,Mobility+0x1C,Handling+0x20,MagazineSize+0x24,RoundsPerMinute+0x28) from a stats struct embedded at+0x30of the query object (the debug stringsWeapon Recoil,Weapon Handlingand the others sit in the same function). The two uses are display only and are not part of the fire loop (derived, confidence high; the call at0x6F1C616stores the RPM unchanged intoRoundsPerMinute+0x28, which feedsFireRateText). The call at0x6F1C57Bfeeds theRecoildisplay stat:Recoil = RPM * mean_recoil * 0.5(mulssat0x6F1C5A4, constant 0.5 at RVA0x9AC3D40, stored at[rbp-0x58]= query+0x18), wheremean_recoilis the sum, over the firstMagazineSizeshots (query-object+0xE80, minimum 1), of the absolute values of both components of eachRecoilPatternentry (stats struct+0xCA0), with every shot beyond the pattern’s length counted as the width|max - min|ofPostPatternRandomYawRecoil(+0xCC0) and ofPostPatternRandomPitchRecoil(+0xCD0), divided by that shot count. Inferred: the number shown is a recoil-per-second index, not a per-shot angle. Which stat bar uses it was not traced (UI). - Attributes, effects, attachments, ADS and stance. No code path found that carries one of these into the interval. The component-level modifier fields of the weapon component are bullet speed only (
InitialBulletSpeedModifier,+0x114); the weapon attribute names and gameplay effects indata/contain nothing about fire rate (see above); attachments do not callInitWeapon(round 20). ADS and stance state were not seen in start-fire or the post-shot routine as inputs to the interval: start-fire reads only the RPM, world time, last-shot time (+0xFE8, written at0x6B94597) and the fire-mode byte. An attribute set could still reach the stats struct through a path that was not decoded, so this is static-only, bounded to the ranges below. - Ranges searched. Whole
.text(0x1000-0x9A27000) by byte pattern forSetTimercalls, getter calls and+0x138/+0x278float operands; capstone on0x6B80000-0x6BC0000; decompiled bodies of0x6B93610(post-shot, burst counter, recoil-reset timer),0x6B94700(fire body, read only for timer, burst and RPM use),0x6B96160,0x6B98560(stop-fire),0x6B953E0and0x6B8FFE0. A byte scan does not see indirect or virtualized dispatch (the protection layer’s stubs are not removed in this image), and 27 of the +0x138/+0x278 hits are in code the corpus has no function record for, so those were ruled out only by the fingerprint window, not read. Result: no second consumer of RPM and no second shot-interval timer found; item data throughInitWeaponremains the only source. Runtime confirmation was not made.
IronSightsAimTime is set on several launchers to 50000 (for example RPG-7 and 9K333). That is not a plausible value in seconds, so the column is left out of the table. TypicalSpeed is present on 24 stats assets. Its relation to InitialBulletSpeed is not stated in the data.
4. Default weapon attributes (WDItemExtensionData_GameplayEffects)
Each weapon stores a list of Attribute.Gameplay.Weapon.* defaults. Five multipliers are identical on all 37 weapons: VerticalRecoilMultiplier 1.0, HorizontalRecoilMultiplier 1.0, AdsZoomMultiplier 1.2, SpreadMultiplier 1.0, AccuracyMultiplier 1.0. Length is 80.0 on every weapon except ID_Launcher_04, which lists it twice (100, then 80); the first value, 100, is installed (see Weapon Attributes §4). The varying attributes are below.
| ID | AimTime | EquipSpeedMult | HolsterSpeedMult | MinZeroingRange | MaxZeroingRange |
|---|---|---|---|---|---|
| A91 | 0.525 | 0.92 | 1 | 100 | 100 |
| AK74M | 0.45 | 1 | 1.05 | 100 | 1e+03 |
| KH2002 | 0.525 | 0.95 | 1 | 100 | 100 |
| M4 | 0.49 | 0.95 | 1.1 | 100 | 500 |
| TAR21 | 0.525 | 0.95 | 1 | 100 | 300 |
| WEPN_029 | 0.49 | 0.95 | 1 | 100 | 400 |
| WEPN_030 | 0.5 | 0.95 | 1 | 100 | 600 |
| WEPN_033 | 0.525 | 0.95 | 1 | 100 | 100 |
| WEPN_037 | 0.48 | 1 | 0.95 | 100 | 200 |
| RFB | 0.52 | 0.92 | 1 | 100 | 300 |
| SKS | 0.53 | 1.15 | 1.1 | 100 | 1e+03 |
| SVDM | 0.53 | 0.97 | 1 | 100 | 1.1e+03 |
| WEPN_034 | 0.53 | 0.97 | 1 | 100 | 500 |
| LMG_02 | 0.73 | 0.95 | 1 | 100 | 1.5e+03 |
| M249 | 0.68 | 0.95 | 0.95 | 200 | 1.2e+03 |
| WEPN_036 | 0.64 | 1 | 1 | 100 | 500 |
| CGM4 | 0.5 | 0.95 | 1 | 100 | 100 |
| Launcher_04 | 0.5 | absent | 1.15 | 100 | 1e+03 |
| MMGL | 0.4 | 0.95 | 1 | 50 | 400 |
| RPG7 | 0.5 | 0.95 | 1 | 100 | 500 |
| Glock17 | 0.2 | 1.2 | 1.1 | 50 | 50 |
| Judge | 0.35 | 0.95 | 1.15 | 25 | 25 |
| WEPN_026 | 0.2 | 0.92 | 1 | 25 | 25 |
| WEPN_027 | 0.25 | 0.9 | 0.85 | 50 | 50 |
| WEPN_032 | 0.2 | 1.2 | 1.15 | 50 | 50 |
| MP9 | 0.25 | 1.12 | 1.2 | 50 | 100 |
| SMG_03 | 0.285 | 0.94 | 1.15 | 50 | 200 |
| Vector | 0.25 | 1 | 1 | 50 | 100 |
| WEPN_028 | 0.285 | 1.05 | 0.9 | 25 | 100 |
| WEPN_031 | 0.285 | 0.94 | 1.15 | 50 | 200 |
| M500 | 0.365 | 0.95 | 1 | 100 | 100 |
| MP43 | 0.35 | 1.25 | 0.95 | 70 | 70 |
| MK22 | 0.675 | 1.05 | 1.17 | 100 | 300 |
| Mosin | 0.65 | 0.94 | 1 | 200 | 800 |
| SR_04 | 0.68 | 0.9 | 1.15 | 100 | 300 |
| SV98 | 0.6 | 0.95 | 1 | 100 | 100 |
| WEPN_035 | 0.5 | 1 | 1 | 100 | 100 |
“absent” means the key is not in that weapon’s list. The engine fallback is not in this folder. AimTime is read as the ADS raise time, and the zeroing values are read in the same units as EffectiveRange. Both readings are inferred.
5. Shared handling data
5a. ADS transition ranges (all weapons)
| Field | Range across weapons | Weapons with field |
|---|---|---|
| OutSettlePlayTime | 0.7-9 | 32 |
| OutSettleWeight | 0.1-0.7 | 33 |
| OutSettlePlayRate | 0.64 | 30 |
| TransitionVelocity | 0.14-1 | 33 |
| TransitionDamping | 0.5-0.7 | 33 |
| TransitionStrength | 0.8-1.8 | 26 |
| AttachmentOffsetAlphaLerpSpeed | 15-34 | 26 |
| AttachmentEndRange | 0.9-1 | 17 |
| CameraOffsetLerpSpeed | 30-999 | 31 |
| MinWallCollisionAlpha | 0.75-1 | 18 |
| MinBlockAdsWallCollisionAlpha | 0.01-0.3 | 17 |
| SpringLerpWeight | 0.1 | 5 |
5b. Spread, stance and holdable
- Spread: every weapon has three
SpreadValuesentries (EBHPlayerStance::Stand,Crouch,Prone). Each points at a row ofCT_Weapon_Spreadnamed<Class>.<Stance>(for exampleAR.StandorSniper.Prone). All stance multipliers are1.0, so the live spread numbers are in the curve table, not here. How the curve is evaluated (X is the planar speed over a per-stance reference speed, the result is a half-angle in degrees, smoothed at 25 per second, zero when fully aimed) is on Weapon Spread §6. - Stance recoil: weapons with a
StanceGameplayEffectslist point atGE_Weapon_StanceRecoil_<Class>_<Stance>effects, with a separate set per class. Launcher_04 has no list. - Holdable:
MeshFovAdsis 80.0 on 34 of the 37 weapons and 75.0 on the other 3. Hip offset (ItemHipOffset) varies per weapon. It is absent onLauncher_04,RPG7andWEPN_033throughWEPN_037. - Trace radius (
WeaponTraceRadius): 6.0 on most rifles, 8.0 on Launcher_04, 4.0 on the Deagle, 3.0 on the Judge.
5c. Input bindings
Every weapon bundle carries a B_DefaultWeaponInputData_C. Across the bundles there are 14 distinct bindings. The first 13 appear on all 37 weapons: Reload, SecondaryUse (and .Release), MeleeBash, Player.PrimaryUse (and .Release), CycleSights, CycleFireMode, IncreaseZeroing, DecreaseZeroing, Inspect, Bipod.ToggleFold, Bipod.Mount. Reload.Discard appears on 28.
The WDItemInputBehavior_ActivateAction objects (435 in total) export with empty properties, so what each action does is not visible in this folder.
5d. Ammo model
Every weapon has one Action.Item.PrimaryUse requirement. It spends ResourcesUsed = 1 of RequiredResource per shot, restricted to the weapon’s own items (bLimitSearchToItem = true). The resource type is Item.Resource.Magazine (fed from a magazine) or Item.Resource.Ammo (loose). Section 2 gives both per weapon.
5e. Aim assist, audio and mutables
- Aim assist: 7 settings assets, one per class (
DA_AimAssistSettings_<Class>), all sharingDA_AimAssistFilter. Weapon count per settings asset: AR 10, SR 9, Pistol 5, RPG 4, SMG 5, Shotgun 2, LMG 2. - Audio: each weapon has one
WDAudioDatawith between 31 and 51 sound cues (SoundAssets). - Mutable: all 37 weapons point at
/Game/Items/Weapons/Mutable/MUT_MasterWeapon. Each carries aDefaultParameterslist of the slots it exposes:Id.Item(37),Item.Attachment.Handguard(35),Sight(34),Barrel(34),Muzzle(33),Grip(32),Magazine(32),Mount{Left,Right,Top,Under}(32 each),Stock(32),Underbarrel(32),CantedSight(31),SightMount(30),DustCover(2). The parameter values are not decoded.
6. Attachments and components
Grouped by folder. “Vendor-priced” counts items that have a WDVendorData row. The components have none.
| Folder | Items | Vendor-priced | Shown price | Unlock lvl | With GE effect | Attachment type tags |
|---|---|---|---|---|---|---|
| Muzzle | 49 | 46 | 350-1400 | 1 | 48 | Muzzle |
| Magazines | 34 | 33 | 10-280 | 0,1 | 33 | Magazine |
| Barrels | 25 | 0 | - | - | 0 | Barrel |
| Sights | 21 | 19 | 200-1800 | 1 | 21 | Sight |
| Stocks | 21 | 0 | - | - | 0 | Stock |
| Handguards | 20 | 0 | - | - | 0 | Handguard, Stock |
| Foregrips | 15 | 15 | 540-1480 | 1 | 15 | Bipod, Grip |
| Pistol Grips | 15 | 0 | - | - | 0 | Grip, None |
| Dust Covers | 3 | 0 | - | - | 0 | None |
| Triggers | 3 | 0 | - | - | 0 | Handguard, None |
Every vendor-priced attachment carries a GE_* gameplay effect in AppliedEffects. The numeric effects are inside those GE_* assets, which are not in this folder. Per-attachment stat deltas therefore cannot be given from this dump. Per-item price, unlock level, name, slot and fitting weapons are tabulated in Weapon Shop and Parts.
Magazine class tags (on the item context, across all item definitions): Extended (12), Standard (10), Small (7), Drum (4), Belt (3), Internal (1). These are tags only. Round counts appear in item names, for example “AK74 30 RND Magazine”.
Shotgun choke override: some muzzles set bOverrideShotgunData. For example, MUZL_012 (MZL_07, AR Multi-Caliber Suppressor) sets ChokeFactor 0.8, OuterConeHalfAngle 10, InnerConeHalfAngleMultiplier 0.5, and FireMuzzleFlashScaleMultiplier 0.2.
Attachment type tags are inconsistent: two of three triggers (TRGR_002, TRGR_003) are tagged Item.Attachment.Handguard. Four stocks in Handguards are tagged Item.Attachment.Stock. Two pistol grips and one trigger have no attachment type. The effect of this on slot fitting is not verified.
Multiple vendor variations: magazine vendor rows hold 7 variations each. The first is shown (bShowInVendor = true) and the rest are hidden price points. For example, MAGZ_002 shows 20 and hides 27, 27, 30, 30, 34, 34. The hidden variations are not decoded.
Weight: attachments range from 0.03 (sights) to 2.2 (magazines). Foregrips range 0.08 to 0.79.
7. Bundle wiring
GameFeatureData(321): one per bundle. Each scans its own/<Bundle>/Datafolder forBH.Itemassets, markedAlwaysCook.GameFeatureAction_DataRegistrySource(160): registersDT_VendorData_Ext_*into theVendorDataregistry andDT_EntityData_Ext_*intoEntityData_UI. Both are client and server sources.GameFeatureAction_AddWorldPartitionContent(303): adds an External Data Layer, for exampleEDL_AR_01.- Entity-UI rows (
BHEntityData_UI, 133 tables) hold display name (string-table key), description, category tag and icons. Vendor rows (WDVendorData, 150 tables) hold the variations described above.
8. Anomalies and data-quality notes
- WEPN_033 stats asset is a copy.
DA_WEPN_033_StatshasWeaponId = Id.Item.TAR21and values 700 RPM, 71,500 speed, 300 range, which match the A-91 family.TAR21already has its own stats asset (DA_TAR21_Stats, 750 RPM, 91,000 speed, 550 range). TheWEPN_033item loads its own file, so its live values are the copied ones. - Missing stats assets.
Pistol_02(Judge) referencesDA_Judge_StatsandWEPN_007(SKS) referencesDA_SKS_Stats. Neither exists in this folder, so their ballistics are unknown here. - Zero shown prices.
KH2002,A91andWEPN_033are shown at price 0 with unlock level 1. The data does not say whether this means unreleased, free or unpriced. - Launcher
IronSightsAimTime = 50000. Not a time value. Meaning unknown. - Attachment type tags are inconsistent (section 6).
- No vendor row for 93 of 206 parts. That is all 87
Componentsitems, plus 6 attachments (3 muzzles, 2 sights and 1 magazine). The 87 components are probably weapon-specific and unlocked by ownership, but the unlock path is not in this folder. - Archetype tag differs from class.
WEPN_037(TCX-SPR) has damage classARbut archetype tagItem.Archetype.DMR. The table keeps both values as exported.
9. How the values combine
- Price and unlock: the shown vendor variation sets the price and
LevelToUnlock. Hidden variations never appear in the shop. - Attachments: a fitted attachment applies the
GE_*effects in itsAppliedEffects. Those effects are not in this folder, so the net stat change of a build cannot be computed here. - Ammo: one unit of
RequiredResourceis spent per primary-fire shot, from the weapon’s own magazine or ammo pool. - Spread and recoil: base values sit in
WDWeaponStatsDataand theCT_Weapon_Spreadcurve table. Attribute multipliers (all 1.0 exceptAdsZoomMultiplierat 1.2) apply on top. The runtime combination is on Weapon Spread.
10. Still not pulled / limitations
GE_*gameplay effects (attachment stat changes, stance recoil modifiers, default attribute effects) are referenced but not in this folder. Numeric attachment effects are the largest gap.- Curves: ADS, recoil, view kick, sway and falloff curves are present as
EditorCurveDatakeyframes insideWDWeaponStatsData, and as 7CurveFloatobjects. They are not decoded. WDItemInputBehavior_ActivateActionexports with empty payloads, so the action logic is not visible.- Mutable parameters (
MUT_MasterWeapon) are listed by key only. Option lists and default textures are not summarized. - Camera data (
WDItemsmithDataFragment_CameraData, 36) and animation sets (BHAnimationSet, 45) are counted but not decoded. - Missing stats: Judge and SKS (section 8).
- Units and meanings of accuracy, deviation, focus energy, recoil relief,
TypicalSpeed,AimTimeand the zeroing values are inferred from property names. - Outside this folder: the
Content/Weaponstree and theGameMechanicsdamage and reward tables are not covered here.