dcso3/
unit.rs

1/*
2Copyright 2024 Eric Stokes.
3
4This file is part of dcso3.
5
6dcso3 is free software: you can redistribute it and/or modify it under
7the terms of the MIT License.
8
9dcso3 is distributed in the hope that it will be useful, but WITHOUT
10ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11FITNESS FOR A PARTICULAR PURPOSE.
12*/
13
14use super::{as_tbl, controller::Controller, cvt_err, group::Group, object::Object, String};
15use crate::{
16    env::miz::UnitId,
17    net::SlotId,
18    object::{DcsObject, DcsOid},
19    record_perf, simple_enum, wrapped_table, LuaEnv, LuaVec2, LuaVec3, MizLua, Position3, Sequence,
20};
21use anyhow::{bail, Result};
22use log::warn;
23use mlua::{prelude::*, Value};
24use na::Vector2;
25use serde_derive::{Deserialize, Serialize};
26use std::{marker::PhantomData, ops::Deref};
27
28simple_enum!(UnitCategory, u8, [
29    Airplane => 0,
30    GroundUnit => 2,
31    Helicopter => 1,
32    Ship => 3,
33    Structure => 4
34]);
35
36wrapped_table!(Ammo, None);
37
38impl<'lua> Ammo<'lua> {
39    pub fn count(&self) -> Result<u32> {
40        Ok(self.t.raw_get("count")?)
41    }
42
43    pub fn type_name(&self) -> Result<String> {
44        Ok(self.t.raw_get::<_, LuaTable>("desc")?.raw_get("typeName")?)
45    }
46
47    pub fn display_name(&self) -> Result<String> {
48        Ok(self
49            .t
50            .raw_get::<_, LuaTable>("desc")?
51            .raw_get("displayName")?)
52    }
53}
54
55wrapped_table!(Unit, Some("Unit"));
56
57impl<'lua> Unit<'lua> {
58    pub fn get_by_name(lua: MizLua<'lua>, name: &str) -> Result<Unit<'lua>> {
59        let globals = lua.inner().globals();
60        let unit = as_tbl("Unit", None, globals.raw_get("Unit")?)?;
61        Ok(record_perf!(
62            unit_get_by_name,
63            unit.call_function("getByName", name)?
64        ))
65    }
66
67    pub fn is_exist(&self) -> Result<bool> {
68        Ok(record_perf!(
69            unit_is_exist,
70            self.t.call_method("isExist", ())?
71        ))
72    }
73
74    pub fn destroy(self) -> Result<()> {
75        Ok(self.t.call_method("destroy", ())?)
76    }
77
78    pub fn get_desc(&self) -> Result<mlua::Table<'lua>> {
79        Ok(self.t.call_method("getDesc", ())?)
80    }
81
82    pub fn as_object(&self) -> Result<Object<'lua>> {
83        Ok(Object::from_lua(Value::Table(self.t.clone()), self.lua)?)
84    }
85
86    pub fn get_type_name(&self) -> Result<String> {
87        Ok(self.t.call_method("getTypeName", ())?)
88    }
89
90    pub fn get_point(&self) -> Result<LuaVec3> {
91        Ok(record_perf!(get_point, self.t.call_method("getPoint", ())?))
92    }
93
94    pub fn get_position(&self) -> Result<Position3> {
95        Ok(record_perf!(
96            get_position,
97            self.t.call_method("getPosition", ())?
98        ))
99    }
100
101    pub fn get_ground_position(&self) -> Result<LuaVec2> {
102        let pos = self.get_point()?;
103        Ok(LuaVec2(Vector2::from(na::Vector2::new(pos.0.x, pos.0.z))))
104    }
105
106    pub fn get_velocity(&self) -> Result<LuaVec3> {
107        Ok(record_perf!(
108            get_velocity,
109            self.t.call_method("getVelocity", ())?
110        ))
111    }
112
113    pub fn in_air(&self) -> Result<bool> {
114        Ok(record_perf!(in_air, self.t.call_method("inAir", ())?))
115    }
116
117    pub fn is_active(&self) -> Result<bool> {
118        Ok(self.t.call_method("isActive", ())?)
119    }
120
121    pub fn get_name(&self) -> Result<String> {
122        Ok(self.t.call_method("getName", ())?)
123    }
124
125    pub fn get_player_name(&self) -> Result<Option<String>> {
126        Ok(self.t.call_method("getPlayerName", ())?)
127    }
128
129    pub fn id(&self) -> Result<UnitId> {
130        Ok(self.t.call_method("getID", ())?)
131    }
132
133    pub fn slot(&self) -> Result<SlotId> {
134        Ok(SlotId::from(self.id()?))
135    }
136
137    pub fn get_number(&self) -> Result<i64> {
138        Ok(self.t.call_method("getNumber", ())?)
139    }
140
141    pub fn get_controller(&self) -> Result<Controller<'lua>> {
142        Ok(self.t.call_method("getController", ())?)
143    }
144
145    pub fn get_group(&self) -> Result<Group<'lua>> {
146        Ok(self.t.call_method("getGroup", ())?)
147    }
148
149    pub fn get_callsign(&self) -> Result<String> {
150        Ok(self.t.call_method("getCallsign", ())?)
151    }
152
153    pub fn get_life(&self) -> Result<i32> {
154        Ok(self.t.call_method("getLife", ())?)
155    }
156
157    pub fn get_life0(&self) -> Result<i32> {
158        Ok(self.t.call_method("getLife0", ())?)
159    }
160
161    pub fn get_fuel(&self) -> Result<f32> {
162        Ok(self.t.call_method("getFuel", ())?)
163    }
164
165    pub fn enable_emission(&self, on: bool) -> Result<()> {
166        Ok(self.t.call_method("enableEmission", on)?)
167    }
168
169    pub fn get_category(&self) -> Result<UnitCategory> {
170        Ok(self.t.call_method("getCategory", ())?)
171    }
172
173    pub fn get_ammo(&self) -> Result<Sequence<'lua, Ammo<'lua>>> {
174        Ok(record_perf!(get_ammo, self.t.call_method("getAmmo", ())?))
175    }
176}
177
178#[derive(Debug, Clone)]
179pub struct ClassUnit;
180
181impl<'lua> DcsObject<'lua> for Unit<'lua> {
182    type Class = ClassUnit;
183
184    fn object_id(&self) -> Result<DcsOid<Self::Class>> {
185        Ok(DcsOid {
186            id: self.raw_get("id_")?,
187            class: "Unit".into(),
188            t: PhantomData,
189        })
190    }
191
192    fn get_instance(lua: MizLua<'lua>, id: &DcsOid<Self::Class>) -> Result<Self> {
193        let t = lua.inner().create_table()?;
194        t.set_metatable(Some(lua.inner().globals().raw_get(&**id.class)?));
195        t.raw_set("id_", id.id)?;
196        let t = Unit {
197            t,
198            lua: lua.inner(),
199        };
200        if !t.is_exist()? {
201            warn!("{} is an invalid unit", id.id);
202            bail!("{} is an invalid unit", id.id)
203        }
204        // work around DCS bug that results in isExist => true for dead units
205        if t.get_life()? <= 0 {
206            warn!("{} is dead", id.id);
207            bail!("{} is dead", id.id)
208        }
209        Ok(t)
210    }
211
212    fn get_instance_dyn<T>(lua: MizLua<'lua>, id: &DcsOid<T>) -> Result<Self> {
213        id.check_implements(lua, "Unit")?;
214        let id = DcsOid {
215            id: id.id,
216            class: id.class.clone(),
217            t: PhantomData,
218        };
219        Self::get_instance(lua, &id)
220    }
221
222    fn change_instance(self, id: &DcsOid<Self::Class>) -> Result<Self> {
223        self.raw_set("id_", id.id)?;
224        if !self.is_exist()? {
225            warn!("{} is an invalid unit", id.id);
226            bail!("{} is an invalid unit", id.id)
227        }
228        // work around DCS bug that results in isExist => true for dead units
229        if self.get_life()? <= 0 {
230            warn!("{} is dead", id.id);
231            bail!("{} is dead", id.id)
232        }
233        Ok(self)
234    }
235
236    fn change_instance_dyn<T>(self, id: &DcsOid<T>) -> Result<Self> {
237        id.check_implements(MizLua(self.lua), "Unit")?;
238        self.t.raw_set("id_", id.id)?;
239        if !self.is_exist()? {
240            warn!("{} is an invalid unit", id.id);
241            bail!("{} is an invalid unit", id.id)
242        }
243        // work around DCS bug that results in isExist => true for dead units
244        if self.get_life()? <= 0 {
245            warn!("{} is dead", id.id);
246            bail!("{} is dead", id.id)
247        }
248        Ok(self)
249    }
250}