dcso3/
object.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, 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}