tf2_enum/
stock_weapon.rs

1use crate::{Class, HasItemDefindex, ItemSlot};
2use num_enum::{TryFromPrimitive, TryFromPrimitiveError};
3use serde::{Deserialize, Serialize};
4use strum::{Display, EnumCount, EnumIter, EnumString};
5
6/// Stock weapons.
7#[derive(
8    Debug,
9    Clone,
10    Copy,
11    Eq,
12    PartialEq,
13    Ord,
14    PartialOrd,
15    Hash,
16    Display,
17    Deserialize,
18    Serialize,
19    EnumString,
20    EnumIter,
21    EnumCount,
22)]
23#[allow(missing_docs)]
24pub enum StockWeapon {
25    #[strum(serialize = "Bat")]
26    Bat,
27    #[strum(serialize = "Bottle")]
28    Bottle,
29    #[strum(serialize = "Fire Axe")]
30    FireAxe,
31    #[strum(serialize = "Kukri")]
32    Kukri,
33    #[strum(serialize = "Knife")]
34    Knife,
35    #[strum(serialize = "Fists")]
36    Fists,
37    #[strum(serialize = "Shovel")]
38    Shovel,
39    #[strum(serialize = "Wrench")]
40    Wrench,
41    #[strum(serialize = "Bonesaw")]
42    Bonesaw,
43    #[strum(serialize = "Shotgun")]
44    Shotgun,
45    #[strum(serialize = "Scattergun")]
46    Scattergun,
47    #[strum(serialize = "Sniper Rifle")]
48    SniperRifle,
49    #[strum(serialize = "Minigun")]
50    Minigun,
51    #[strum(serialize = "SMG")]
52    SMG,
53    #[strum(serialize = "Syringe Gun")]
54    SyringeGun,
55    #[strum(serialize = "Rocket Launcher")]
56    RocketLauncher,
57    #[strum(serialize = "Grenade Launcher")]
58    GrenadeLauncher,
59    #[strum(serialize = "Stickybomb Launcher")]
60    StickybombLauncher,
61    #[strum(serialize = "Flame Thrower")]
62    FlameThrower,
63    #[strum(serialize = "Pistol")]
64    Pistol,
65    #[strum(serialize = "Revolver")]
66    Revolver,
67    #[strum(serialize = "Construction PDA")]
68    ConstructionPDA,
69    #[strum(serialize = "Destruction PDA")]
70    DestructionPDA,
71    #[strum(serialize = "Disguise Kit")]
72    DisguiseKit,
73    #[strum(serialize = "PDA")]
74    PDA,
75    #[strum(serialize = "Medi Gun")]
76    MediGun,
77    #[strum(serialize = "Invis Watch")]
78    InvisWatch,
79}
80
81impl HasItemDefindex for StockWeapon {
82    /// Gets the defindex for this item. If there are multiple, the first is returned.
83    /// 
84    /// If you want the defindex associated with inventory items, use
85    /// [`StockWeapon::econ_defindex`].
86    fn defindex(&self) -> u32 {
87        self.defindexes()
88            .first()
89            .copied()
90            .unwrap() // safety - we know there is at least one defindex
91    }
92    
93    /// Attempts to create a [`StockWeapon`] from a defindex. Excludes definitions for skinned
94    /// items.
95    fn from_defindex(defindex: u32) -> Option<Self> {
96        match defindex {
97            0 | 190 => Some(Self::Bat),
98            1 | 191 => Some(Self::Bottle),
99            2 | 192 => Some(Self::FireAxe),
100            3 | 193 => Some(Self::Kukri),
101            4 | 194 => Some(Self::Knife),
102            5 | 195 => Some(Self::Fists),
103            6 | 196 => Some(Self::Shovel),
104            7 | 197 => Some(Self::Wrench),
105            8 | 198 => Some(Self::Bonesaw),
106            9 | 10 | 11 | 12 | 199 => Some(Self::Shotgun),
107            13 | 200 => Some(Self::Scattergun),
108            14 | 201 => Some(Self::SniperRifle),
109            15 | 202 => Some(Self::Minigun),
110            16 | 203 => Some(Self::SMG),
111            17 | 204 => Some(Self::SyringeGun),
112            18 | 205 => Some(Self::RocketLauncher),
113            19 | 206 => Some(Self::GrenadeLauncher),
114            20 | 207 => Some(Self::StickybombLauncher),
115            21 | 208 => Some(Self::FlameThrower),
116            22 | 23 | 209 => Some(Self::Pistol),
117            24 | 210 => Some(Self::Revolver),
118            25 | 737 => Some(Self::ConstructionPDA),
119            26 => Some(Self::DestructionPDA),
120            27 => Some(Self::DisguiseKit),
121            28 => Some(Self::PDA),
122            29 | 211 => Some(Self::MediGun),
123            30 | 212 => Some(Self::InvisWatch),
124            _ => None,
125        }
126    }
127}
128
129impl StockWeapon {
130    /// Gets the set of related defindexes of the weapon. Excludes definitions for skinned items.
131    pub fn defindexes(&self) -> &'static [u32] {
132        match self {
133            Self::Bat => &[0, 190],
134            Self::Bottle => &[1, 191],
135            Self::FireAxe => &[2, 192],
136            Self::Kukri => &[3, 193],
137            Self::Knife => &[4, 194],
138            Self::Fists => &[5, 195],
139            Self::Shovel => &[6, 196],
140            Self::Wrench => &[7, 197],
141            Self::Bonesaw => &[8, 198],
142            Self::Shotgun => &[9, 10, 11, 12, 199],
143            Self::Scattergun => &[13, 200],
144            Self::SniperRifle => &[14, 201],
145            Self::Minigun => &[15, 202],
146            Self::SMG => &[16, 203],
147            Self::SyringeGun => &[17, 204],
148            Self::RocketLauncher => &[18, 205],
149            Self::GrenadeLauncher => &[19, 206],
150            Self::StickybombLauncher => &[20, 207],
151            Self::FlameThrower => &[21, 208],
152            Self::Pistol => &[22, 23, 209],
153            Self::Revolver => &[24, 210],
154            Self::ConstructionPDA => &[25, 737],
155            Self::DestructionPDA => &[26],
156            Self::DisguiseKit => &[27],
157            Self::PDA => &[28],
158            Self::MediGun => &[29, 211],
159            Self::InvisWatch => &[30, 212],
160        }
161    }
162    
163    /// Gets the economy defindex for this weapon (if available).
164    /// 
165    /// This is the defindex that can be mapped to inventory items. Not available for
166    /// [`StockWeapon::DestructionPDA`], [`StockWeapon::PDA`], and [`StockWeapon::DisguiseKit`].
167    pub fn econ_defindex(&self) -> Option<u32> {
168        match self {
169            Self::Bat => Some(190),
170            Self::Bottle => Some(191),
171            Self::FireAxe => Some(192),
172            Self::Kukri => Some(193),
173            Self::Knife => Some(194),
174            Self::Fists => Some(195),
175            Self::Shovel => Some(196),
176            Self::Wrench => Some(197),
177            Self::Bonesaw => Some(198),
178            Self::Shotgun => Some(199),
179            Self::Scattergun => Some(200),
180            Self::SniperRifle => Some(201),
181            Self::Minigun => Some(202),
182            Self::SMG => Some(203),
183            Self::SyringeGun => Some(204),
184            Self::RocketLauncher => Some(205),
185            Self::GrenadeLauncher => Some(206),
186            Self::StickybombLauncher => Some(207),
187            Self::FlameThrower => Some(208),
188            Self::Pistol => Some(209),
189            Self::Revolver => Some(210),
190            _ => None,
191        }
192    }
193
194    /// Gets the set of classes that can use this weapon.
195    pub fn used_by_classes(&self) -> &'static [Class] {
196        match self {
197            Self::Bat |
198            Self::Scattergun => &[Class::Scout],
199            Self::Bottle |
200            Self::GrenadeLauncher |
201            Self::StickybombLauncher => &[Class::Demoman],
202            Self::FireAxe |
203            Self::FlameThrower => &[Class::Pyro],
204            Self::Kukri |
205            Self::SniperRifle |
206            Self::SMG => &[Class::Sniper],
207            Self::Knife |
208            Self::Revolver |
209            Self::DisguiseKit |
210            Self::InvisWatch => &[Class::Spy],
211            Self::Fists |
212            Self::Minigun => &[Class::Heavy],
213            Self::Shovel |
214            Self::RocketLauncher => &[Class::Soldier],
215            Self::Wrench  |
216            Self::ConstructionPDA |
217            Self::DestructionPDA |
218            Self::PDA => &[Class::Engineer],
219            Self::Bonesaw |
220            Self::SyringeGun |
221            Self::MediGun => &[Class::Medic],
222            Self::Shotgun => &[Class::Soldier, Class::Pyro, Class::Engineer, Class::Heavy],
223            Self::Pistol => &[Class::Scout, Class::Engineer],
224        }
225    }
226    
227    /// Checks if the weapon is used by a specific class.
228    pub fn used_by_class(&self, class: Class) -> bool {
229        self.used_by_classes().contains(&class)
230    }
231    
232    /// Gets the stock weapons available to a specific class.
233    pub fn class_stock_weapons(class: Class) -> &'static [StockWeapon] {
234        match class {
235            Class::Scout => &[Self::Scattergun, Self::Pistol, Self::Bat],
236            Class::Demoman => &[Self::GrenadeLauncher, Self::StickybombLauncher, Self::Bottle],
237            Class::Pyro => &[Self::FlameThrower, Self::Shotgun, Self::FireAxe],
238            Class::Sniper => &[Self::SniperRifle, Self::SMG, Self::Kukri],
239            Class::Spy => &[Self::Revolver, Self::Knife, Self::DisguiseKit, Self::InvisWatch],
240            Class::Heavy => &[Self::Minigun, Self::Shotgun, Self::Fists],
241            Class::Soldier => &[Self::RocketLauncher, Self::Shotgun, Self::Shovel],
242            Class::Engineer => &[Self::Shotgun, Self::Pistol, Self::Wrench, Self::ConstructionPDA, Self::DestructionPDA],
243            Class::Medic => &[Self::SyringeGun, Self::MediGun, Self::Bonesaw],
244        }
245    }
246    
247    /// Gets the item slot of the weapon for the class.
248    /// 
249    /// **Note:** [`StockWeapon::Shotgun`] is a secondary weapon for the Soldier and Pyro classes
250    /// but a primary weapon for the Engineer class. It's returned as [`ItemSlot::Secondary`] here.
251    pub fn item_slot(&self) -> ItemSlot {
252        match self {
253            Self::Bat => ItemSlot::Melee,
254            Self::Scattergun => ItemSlot::Primary,
255            Self::Bottle => ItemSlot::Secondary,
256            Self::FireAxe => ItemSlot::Melee,
257            Self::FlameThrower => ItemSlot::Primary,
258            Self::Kukri => ItemSlot::Melee,
259            Self::Knife => ItemSlot::Melee,
260            Self::Fists => ItemSlot::Melee,
261            Self::Shovel => ItemSlot::Melee,
262            Self::Wrench => ItemSlot::Melee,
263            Self::Bonesaw => ItemSlot::Melee,
264            Self::SMG => ItemSlot::Secondary,
265            Self::SyringeGun => ItemSlot::Primary,
266            Self::RocketLauncher => ItemSlot::Primary,
267            Self::GrenadeLauncher => ItemSlot::Primary,
268            Self::StickybombLauncher => ItemSlot::Secondary,
269            Self::SniperRifle => ItemSlot::Primary,
270            Self::Minigun => ItemSlot::Primary,
271            Self::Pistol => ItemSlot::Secondary,
272            Self::Revolver => ItemSlot::Primary,
273            Self::DisguiseKit => ItemSlot::PDA,
274            Self::InvisWatch => ItemSlot::PDA2,
275            Self::MediGun => ItemSlot::Secondary,
276            Self::ConstructionPDA => ItemSlot::PDA,
277            Self::DestructionPDA => ItemSlot::PDA2,
278            Self::PDA => ItemSlot::Building,
279            Self::Shotgun => ItemSlot::Secondary,
280        }
281    }
282}
283
284// Manually implemented since TryFromPrimitive only supports mapping each variant to one number.
285impl TryFromPrimitive for StockWeapon {
286    const NAME: &'static str = "StockWeapon";
287    type Primitive = u32;
288    type Error = TryFromPrimitiveError<StockWeapon>;
289    
290    fn try_from_primitive(number: u32) -> Result<Self, Self::Error> {
291        Self::from_defindex(number)
292            .ok_or(TryFromPrimitiveError { number })
293    }
294}
295
296impl TryFrom<u32> for StockWeapon {
297    type Error = TryFromPrimitiveError<StockWeapon>;
298    
299    fn try_from(value: u32) -> Result<Self, Self::Error> {
300        Self::try_from_primitive(value)
301    }
302}
303
304impl TryFrom<&u32> for StockWeapon {
305    type Error = TryFromPrimitiveError<StockWeapon>;
306    
307    fn try_from(value: &u32) -> Result<Self, Self::Error> {
308        Self::try_from_primitive(*value)
309    }
310}
311
312#[cfg(test)]
313mod tests {
314    use super::*;
315    
316    #[test]
317    fn tries_from_primitive() {
318        let weapon = StockWeapon::try_from(0).unwrap();
319        
320        assert_eq!(weapon, StockWeapon::Bat);
321    }
322}