1use super::{as_tbl, cvt_err, unit::Unit, weapon::Weapon, LuaVec3, Position3, String};
15use crate::{
16 check_implements, record_perf, simple_enum, static_object::StaticObject, wrapped_table, LuaEnv,
17 MizLua,
18};
19use anyhow::{anyhow, bail, Result};
20use core::fmt;
21use mlua::{prelude::*, Value};
22use serde_derive::{Deserialize, Serialize};
23use std::{hash::Hash, marker::PhantomData, ops::Deref};
24
25#[derive(Clone, Serialize, Deserialize)]
26pub struct DcsOid<T> {
27 pub(crate) id: u64,
28 pub(crate) class: String,
29 #[serde(skip)]
30 pub(crate) t: PhantomData<T>,
31}
32
33impl<T> DcsOid<T> {
34 pub fn erased(&self) -> DcsOid<ClassObject> {
35 DcsOid {
36 id: self.id,
37 class: self.class.clone(),
38 t: PhantomData,
39 }
40 }
41
42 pub fn check_implements(&self, lua: MizLua, class: &str) -> Result<()> {
43 let m = lua.inner().globals().raw_get(&**self.class)?;
44 if !check_implements(&m, class) {
45 bail!("{:?} is does not implement {class}", self)
46 }
47 Ok(())
48 }
49}
50
51impl<T> fmt::Debug for DcsOid<T> {
52 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53 write!(f, "{{ id: {}, class: {} }}", self.id, self.class)
54 }
55}
56
57impl<T> Hash for DcsOid<T> {
58 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
59 self.id.hash(state)
60 }
61}
62
63impl<T> PartialEq for DcsOid<T> {
64 fn eq(&self, other: &Self) -> bool {
65 self.id.eq(&other.id)
66 }
67}
68
69impl<T> Eq for DcsOid<T> {}
70
71impl<T> PartialOrd for DcsOid<T> {
72 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
73 self.id.partial_cmp(&other.id)
74 }
75}
76
77impl<T> Ord for DcsOid<T> {
78 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
79 self.id.cmp(&other.id)
80 }
81}
82
83#[derive(Debug, Clone)]
84pub struct ClassObject;
85
86pub trait DcsObject<'lua>: Sized + Deref<Target = mlua::Table<'lua>> {
87 type Class: fmt::Debug + Clone;
88
89 fn object_id(&self) -> Result<DcsOid<Self::Class>> {
90 let id = self.raw_get("id_")?;
91 let m = self
92 .get_metatable()
93 .ok_or_else(|| anyhow!("object with no metatable"))?;
94 let class = m.raw_get("className_")?;
95 Ok(DcsOid {
96 id,
97 class,
98 t: PhantomData,
99 })
100 }
101
102 fn change_instance(self, id: &DcsOid<Self::Class>) -> Result<Self>;
103 fn change_instance_dyn<T>(self, id: &DcsOid<T>) -> Result<Self>;
104 fn get_instance(lua: MizLua<'lua>, id: &DcsOid<Self::Class>) -> Result<Self>;
105 fn get_instance_dyn<T>(lua: MizLua<'lua>, id: &DcsOid<T>) -> Result<Self>;
106}
107
108simple_enum!(ObjectCategory, u8, [
109 Void => 0,
110 Unit => 1,
111 Weapon => 2,
112 Static => 3,
113 Base => 4,
114 Scenery => 5,
115 Cargo => 6
116]);
117
118wrapped_table!(Object, Some("Object"));
119
120impl<'lua> Object<'lua> {
121 pub fn destroy(self) -> Result<()> {
122 Ok(self.t.call_method("destroy", ())?)
123 }
124
125 pub fn get_category(&self) -> Result<ObjectCategory> {
126 Ok(self.t.call_method("getCategory", ())?)
127 }
128
129 pub fn get_desc(&self) -> Result<mlua::Table<'lua>> {
130 Ok(self.t.call_method("getDesc", ())?)
131 }
132
133 pub fn has_attribute(&self, attr: String) -> Result<bool> {
134 Ok(self.t.call_method("hasAttribute", attr)?)
135 }
136
137 pub fn get_name(&self) -> Result<String> {
138 Ok(self.t.call_method("getName", ())?)
139 }
140
141 pub fn get_type_name(&self) -> Result<String> {
142 Ok(self.t.call_method("getTypeName", ())?)
143 }
144
145 pub fn get_point(&self) -> Result<LuaVec3> {
146 Ok(record_perf!(get_point, self.t.call_method("getPoint", ())?))
147 }
148
149 pub fn get_position(&self) -> Result<Position3> {
150 Ok(record_perf!(
151 get_position,
152 self.t.call_method("getPosition", ())?
153 ))
154 }
155
156 pub fn get_velocity(&self) -> Result<LuaVec3> {
157 Ok(record_perf!(
158 get_velocity,
159 self.t.call_method("getVelocity", ())?
160 ))
161 }
162
163 pub fn in_air(&self) -> Result<bool> {
164 Ok(self.t.call_method("inAir", ())?)
165 }
166
167 pub fn is_exist(&self) -> Result<bool> {
168 Ok(self.t.call_method("isExist", ())?)
169 }
170
171 pub fn as_unit(&self) -> Result<Unit<'lua>> {
172 Ok(Unit::from_lua(Value::Table(self.t.clone()), self.lua)?)
173 }
174
175 pub fn as_weapon(&self) -> Result<Weapon<'lua>> {
176 Ok(Weapon::from_lua(Value::Table(self.t.clone()), self.lua)?)
177 }
178
179 pub fn as_static(&self) -> Result<StaticObject<'lua>> {
180 Ok(StaticObject::from_lua(
181 Value::Table(self.t.clone()),
182 self.lua,
183 )?)
184 }
185}
186
187impl<'lua> DcsObject<'lua> for Object<'lua> {
188 type Class = ClassObject;
189
190 fn get_instance(lua: MizLua<'lua>, id: &DcsOid<Self::Class>) -> Result<Self> {
191 let t = lua.inner().create_table()?;
192 t.set_metatable(Some(lua.inner().globals().raw_get(&**id.class)?));
193 t.raw_set("id_", id.id)?;
194 let t = Object {
195 t,
196 lua: lua.inner(),
197 };
198 if !t.is_exist()? {
199 bail!("{} is an invalid object", id.id)
200 }
201 Ok(t)
202 }
203
204 fn get_instance_dyn<T>(lua: MizLua<'lua>, id: &DcsOid<T>) -> Result<Self> {
205 id.check_implements(lua, "Object")?;
206 let id = DcsOid {
207 id: id.id,
208 class: id.class.clone(),
209 t: PhantomData,
210 };
211 Self::get_instance(lua, &id)
212 }
213
214 fn change_instance(self, id: &DcsOid<Self::Class>) -> Result<Self> {
215 self.raw_set("id_", id.id)?;
216 if !self.is_exist()? {
217 bail!("{} is an invalid object", id.id)
218 }
219 Ok(self)
220 }
221
222 fn change_instance_dyn<T>(self, id: &DcsOid<T>) -> Result<Self> {
223 id.check_implements(MizLua(self.lua), "Object")?;
224 self.t.raw_set("id_", id.id)?;
225 if !self.is_exist()? {
226 bail!("{} is an invalid object", id.id)
227 }
228 Ok(self)
229 }
230}