arma_rs/value/loadout/
weapon.rs

1use crate::{FromArma, FromArmaError, IntoArma, Value};
2
3use super::Magazine;
4
5#[derive(Debug, Default, Clone, PartialEq, Eq)]
6/// A primary, secondary, or handgun weapon
7pub struct Weapon(Option<(String, String, String, String, Magazine, Magazine, String)>);
8impl Weapon {
9    /// Create a new weapon
10    #[must_use]
11    pub fn new(class: String) -> Self {
12        Self(Some((
13            class,
14            "".to_string(),
15            "".to_string(),
16            "".to_string(),
17            Magazine::default(),
18            Magazine::default(),
19            "".to_string(),
20        )))
21    }
22
23    /// The weapon slot is occupied
24    #[must_use]
25    pub const fn exists(&self) -> bool {
26        self.0.is_some()
27    }
28
29    /// The class name of the weapon
30    #[must_use]
31    pub fn class(&self) -> Option<&str> {
32        self.0
33            .as_ref()
34            .map(|(class, _, _, _, _, _, _)| class.as_str())
35    }
36
37    /// Set the class name of the weapon
38    pub fn set_class(&mut self, class: String) {
39        if let Some(weapon) = self.0.as_mut() {
40            weapon.0 = class;
41        } else {
42            self.0 = Some((
43                class,
44                "".to_owned(),
45                "".to_owned(),
46                "".to_owned(),
47                Magazine::default(),
48                Magazine::default(),
49                "".to_owned(),
50            ));
51        }
52    }
53
54    /// The class name of the attached suppressor
55    #[must_use]
56    pub fn suppressor(&self) -> Option<&str> {
57        self.0
58            .as_ref()
59            .map(|(_, suppressor, _, _, _, _, _)| suppressor.as_str())
60    }
61
62    /// Set the class name of the attached suppressor
63    /// Returns true if the suppressor was set, false if the weapon was not initialized
64    pub fn set_suppressor(&mut self, suppressor: String) -> bool {
65        if let Some(weapon) = self.0.as_mut() {
66            weapon.1 = suppressor;
67            true
68        } else {
69            false
70        }
71    }
72
73    /// The class name of the attached pointer
74    #[must_use]
75    pub fn pointer(&self) -> Option<&str> {
76        self.0
77            .as_ref()
78            .map(|(_, _, pointer, _, _, _, _)| pointer.as_str())
79    }
80
81    /// Set the class name of the attached pointer
82    /// Returns true if the pointer was set, false if the weapon was not initialized
83    pub fn set_pointer(&mut self, pointer: String) -> bool {
84        if let Some(weapon) = self.0.as_mut() {
85            weapon.2 = pointer;
86            true
87        } else {
88            false
89        }
90    }
91
92    /// The class name of the attached optic
93    #[must_use]
94    pub fn optic(&self) -> Option<&str> {
95        self.0
96            .as_ref()
97            .map(|(_, _, _, optic, _, _, _)| optic.as_str())
98    }
99
100    /// Set the class name of the attached optic
101    /// Returns true if the optic was set, false if the weapon was not initialized
102    pub fn set_optic(&mut self, optic: String) -> bool {
103        if let Some(weapon) = self.0.as_mut() {
104            weapon.3 = optic;
105            true
106        } else {
107            false
108        }
109    }
110
111    /// Get the inserted primary magazine
112    #[must_use]
113    pub fn primary_magazine(&self) -> Option<&Magazine> {
114        self.0.as_ref().map(|(_, _, _, _, primary, _, _)| primary)
115    }
116
117    /// Get the inserted primary magazine mutably
118    pub fn primary_magazine_mut(&mut self) -> Option<&mut Magazine> {
119        self.0.as_mut().map(|(_, _, _, _, primary, _, _)| primary)
120    }
121
122    /// Set the inserted primary magazine
123    /// Returns true if the primary magazine was set, false if the weapon was not initialized
124    pub fn set_primary_magazine(&mut self, primary: Magazine) -> bool {
125        if let Some(weapon) = self.0.as_mut() {
126            weapon.4 = primary;
127            true
128        } else {
129            false
130        }
131    }
132
133    /// Get the inserted secondary magazine
134    #[must_use]
135    pub fn secondary_magazine(&self) -> Option<&Magazine> {
136        self.0
137            .as_ref()
138            .map(|(_, _, _, _, _, secondary, _)| secondary)
139    }
140
141    /// Get the inserted secondary magazine mutably
142    pub fn secondary_magazine_mut(&mut self) -> Option<&mut Magazine> {
143        self.0
144            .as_mut()
145            .map(|(_, _, _, _, _, secondary, _)| secondary)
146    }
147
148    /// Set the inserted secondary magazine
149    /// Returns true if the secondary magazine was set, false if the weapon was not initialized
150    pub fn set_secondary_magazine(&mut self, secondary: Magazine) -> bool {
151        if let Some(weapon) = self.0.as_mut() {
152            weapon.5 = secondary;
153            true
154        } else {
155            false
156        }
157    }
158
159    /// The class name of the attached bipod
160    #[must_use]
161    pub fn bipod(&self) -> Option<&str> {
162        self.0
163            .as_ref()
164            .map(|(_, _, _, _, _, _, bipod)| bipod.as_str())
165    }
166
167    /// Set the class name of the attached bipod
168    /// Returns true if the bipod was set, false if the weapon was not initialized
169    pub fn set_bipod(&mut self, bipod: String) -> bool {
170        if let Some(weapon) = self.0.as_mut() {
171            weapon.6 = bipod;
172            true
173        } else {
174            false
175        }
176    }
177
178    /// Get all classes used on the weapon, including the weapon itself
179    pub fn classes(&self) -> Vec<&str> {
180        let mut classes = Vec::new();
181        if let Some(weapon) = self.0.as_ref() {
182            classes.push(weapon.0.as_str());
183            if !weapon.1.is_empty() {
184                classes.push(weapon.1.as_str());
185            }
186            if !weapon.2.is_empty() {
187                classes.push(weapon.2.as_str());
188            }
189            if !weapon.3.is_empty() {
190                classes.push(weapon.3.as_str());
191            }
192            if let Some(class) = weapon.4.class() {
193                classes.push(class);
194            }
195            if let Some(class) = weapon.5.class() {
196                classes.push(class);
197            }
198            if !weapon.6.is_empty() {
199                classes.push(weapon.6.as_str());
200            }
201        }
202        classes
203    }
204}
205impl FromArma for Weapon {
206    fn from_arma(s: String) -> Result<Self, FromArmaError> {
207        if s == "[]" {
208            return Ok(Self(None));
209        }
210        <(String, String, String, String, Magazine, Magazine, String)>::from_arma(s).map(
211            |(weapon, suppressor, pointer, optic, primary_mag, secondary_mag, bipod)| {
212                Self(Some((
213                    weapon,
214                    suppressor,
215                    pointer,
216                    optic,
217                    primary_mag,
218                    secondary_mag,
219                    bipod,
220                )))
221            },
222        )
223    }
224}
225impl IntoArma for Weapon {
226    fn to_arma(&self) -> Value {
227        self.0.as_ref().map_or_else(
228            || Value::Array(vec![]),
229            |weaon| {
230                Value::Array(vec![
231                    Value::String(weaon.0.clone()),
232                    Value::String(weaon.1.clone()),
233                    Value::String(weaon.2.clone()),
234                    Value::String(weaon.3.clone()),
235                    weaon.4.to_arma(),
236                    weaon.5.to_arma(),
237                    Value::String(weaon.6.clone()),
238                ])
239            },
240        )
241    }
242}
243
244#[cfg(test)]
245mod tests {
246    use super::Weapon;
247    use crate::{loadout::Magazine, FromArma, IntoArma, Value};
248
249    #[test]
250    fn exists() {
251        let weapon = Weapon::default();
252        assert!(!weapon.exists());
253        let weapon = Weapon::new("arifle_Mk20_GL_F".to_owned());
254        assert!(weapon.exists());
255    }
256
257    #[test]
258    fn class() {
259        let weapon = Weapon::default();
260        assert_eq!(weapon.class(), None);
261        let mut weapon = Weapon::new("arifle_Mk20_GL_F".to_owned());
262        assert_eq!(weapon.class(), Some("arifle_Mk20_GL_F"));
263        weapon.set_class("arifle_Mk20_F".to_owned());
264        assert_eq!(weapon.class(), Some("arifle_Mk20_F"));
265    }
266
267    #[test]
268    fn suppressor() {
269        let weapon = Weapon::default();
270        assert_eq!(weapon.suppressor(), None);
271        let mut weapon = Weapon::new("arifle_Mk20_GL_F".to_owned());
272        assert_eq!(weapon.suppressor(), Some(""));
273        weapon.set_suppressor("muzzle_snds_H".to_owned());
274        assert_eq!(weapon.suppressor(), Some("muzzle_snds_H"));
275    }
276
277    #[test]
278    fn pointer() {
279        let weapon = Weapon::default();
280        assert_eq!(weapon.pointer(), None);
281        let mut weapon = Weapon::new("arifle_Mk20_GL_F".to_owned());
282        assert_eq!(weapon.pointer(), Some(""));
283        weapon.set_pointer("acc_pointer_IR".to_owned());
284        assert_eq!(weapon.pointer(), Some("acc_pointer_IR"));
285    }
286
287    #[test]
288    fn optic() {
289        let weapon = Weapon::default();
290        assert_eq!(weapon.optic(), None);
291        let mut weapon = Weapon::new("arifle_Mk20_GL_F".to_owned());
292        assert_eq!(weapon.optic(), Some(""));
293        weapon.set_optic("optic_Hamr".to_owned());
294        assert_eq!(weapon.optic(), Some("optic_Hamr"));
295    }
296
297    #[test]
298    fn primary_magazine() {
299        let weapon = Weapon::default();
300        assert_eq!(weapon.primary_magazine(), None);
301        let mut weapon = Weapon::new("arifle_Mk20_GL_F".to_owned());
302        assert_eq!(weapon.primary_magazine(), Some(&Magazine::default()));
303        weapon.set_primary_magazine(Magazine::new("30Rnd_556x45_Stanag".to_string(), 30));
304        assert_eq!(
305            weapon.primary_magazine(),
306            Some(&Magazine::new("30Rnd_556x45_Stanag".to_string(), 30))
307        );
308    }
309
310    #[test]
311    fn secondary_magazine() {
312        let weapon = Weapon::default();
313        assert_eq!(weapon.secondary_magazine(), None);
314        let mut weapon = Weapon::new("arifle_Mk20_GL_F".to_owned());
315        assert_eq!(weapon.secondary_magazine(), Some(&Magazine::default()));
316        weapon.set_secondary_magazine(Magazine::new("1Rnd_HE_Grenade_shell".to_string(), 1));
317        assert_eq!(
318            weapon.secondary_magazine(),
319            Some(&Magazine::new("1Rnd_HE_Grenade_shell".to_string(), 1))
320        );
321    }
322
323    #[test]
324    fn bipod() {
325        let weapon = Weapon::default();
326        assert_eq!(weapon.bipod(), None);
327        let mut weapon = Weapon::new("arifle_Mk20_GL_F".to_owned());
328        assert_eq!(weapon.bipod(), Some(""));
329        weapon.set_bipod("bipod_01_F".to_owned());
330        assert_eq!(weapon.bipod(), Some("bipod_01_F"));
331    }
332
333    #[test]
334    fn from_arma() {
335        let weapon = Weapon::from_arma("[]".to_owned()).unwrap();
336        assert_eq!(weapon, Weapon::default());
337        let weapon = Weapon::from_arma(
338            "[\"arifle_Mk20_GL_F\",\"\",\"\",\"\",[\"30Rnd_556x45_Stanag\",30],[\"1Rnd_HE_Grenade_shell\",1],\"\"]".to_owned(),
339        )
340        .unwrap();
341        assert_eq!(weapon, {
342            let mut weapon = Weapon::new("arifle_Mk20_GL_F".to_owned());
343            weapon.set_primary_magazine(Magazine::new("30Rnd_556x45_Stanag".to_string(), 30));
344            weapon.set_secondary_magazine(Magazine::new("1Rnd_HE_Grenade_shell".to_string(), 1));
345            weapon
346        });
347    }
348
349    #[test]
350    fn to_arma() {
351        let weapon = Weapon::default();
352        assert_eq!(weapon.to_arma(), Value::Array(vec![]));
353        let weapon = Weapon::new("arifle_Mk20_GL_F".to_owned());
354        assert_eq!(
355            weapon.to_arma(),
356            Value::Array(vec![
357                Value::String("arifle_Mk20_GL_F".to_owned()),
358                Value::String("".to_owned()),
359                Value::String("".to_owned()),
360                Value::String("".to_owned()),
361                Value::Array(vec![]),
362                Value::Array(vec![]),
363                Value::String("".to_owned()),
364            ])
365        );
366    }
367
368    #[test]
369    fn classes() {
370        let weapon = Weapon::from_arma("[]".to_owned()).unwrap();
371        assert_eq!(weapon, Weapon::default());
372        let weapon = Weapon::from_arma(
373            "[\"arifle_Mk20_GL_F\",\"fake_optic\",\"\",\"\",[\"30Rnd_556x45_Stanag\",30],[\"1Rnd_HE_Grenade_shell\",1],\"fake_bipod\"]".to_owned(),
374        )
375        .unwrap();
376        assert_eq!(
377            weapon.classes(),
378            vec![
379                "arifle_Mk20_GL_F",
380                "fake_optic",
381                "30Rnd_556x45_Stanag",
382                "1Rnd_HE_Grenade_shell",
383                "fake_bipod"
384            ]
385        );
386    }
387}