dcso3/
group.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, coalition::Side, controller::Controller, cvt_err, unit::Unit, String};
15use crate::{
16    env::miz::{GroupId, GroupKind},
17    object::{DcsObject, DcsOid, Object},
18    simple_enum, wrapped_table, LuaEnv, MizLua, Sequence,
19};
20use anyhow::{Result, bail};
21use mlua::{prelude::*, Value};
22use serde_derive::{Deserialize, Serialize};
23use std::{marker::PhantomData, ops::Deref};
24
25simple_enum!(GroupCategory, u8, [
26    Airplane => 0,
27    Helicopter => 1,
28    Ground => 2,
29    Ship => 3,
30    Train => 4
31]);
32
33impl GroupCategory {
34    pub fn from_kind(k: GroupKind) -> Option<Self> {
35        match k {
36            GroupKind::Any | GroupKind::Static => None,
37            GroupKind::Plane => Some(Self::Airplane),
38            GroupKind::Helicopter => Some(Self::Helicopter),
39            GroupKind::Vehicle => Some(Self::Ground),
40            GroupKind::Ship => Some(Self::Ship),
41        }
42    }
43}
44
45#[derive(Debug, Clone, Serialize)]
46pub enum Owner {
47    Contested,
48    Side(Side),
49}
50
51impl<'lua> FromLua<'lua> for Owner {
52    fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> LuaResult<Self> {
53        match i64::from_lua(value.clone(), lua)? {
54            3 => Ok(Self::Contested),
55            _ => Ok(Owner::Side(Side::from_lua(value, lua)?)),
56        }
57    }
58}
59
60wrapped_table!(Group, Some("Group"));
61
62impl<'lua> Group<'lua> {
63    pub fn get_by_name(lua: MizLua<'lua>, name: &str) -> Result<Group<'lua>> {
64        let globals = lua.inner().globals();
65        let class = as_tbl("Group", None, globals.raw_get("Group")?)?;
66        let g: Group = class.call_function("getByName", name)?;
67        // work around for dcs bug that can cause getByName to return
68        // a group even though the group is dead
69        if g.get_size()? == 0 {
70            bail!("{} is dead", name)
71        }
72        Ok(g)
73    }
74
75    pub fn is_exist(&self) -> Result<bool> {
76        Ok(self.t.call_method("isExist", ())?)
77    }
78
79    pub fn destroy(self) -> Result<()> {
80        Ok(self.t.call_method("destroy", ())?)
81    }
82
83    pub fn activate(&self) -> Result<()> {
84        Ok(self.t.call_method("activate", ())?)
85    }
86
87    pub fn get_category(&self) -> Result<GroupCategory> {
88        Ok(self.t.call_method("getCategory", ())?)
89    }
90
91    pub fn get_coalition(&self) -> Result<Owner> {
92        Ok(self.t.call_method("getCoalition", ())?)
93    }
94
95    pub fn get_name(&self) -> Result<String> {
96        Ok(self.t.call_method("getName", ())?)
97    }
98
99    pub fn id(&self) -> Result<GroupId> {
100        Ok(self.t.call_method("getID", ())?)
101    }
102
103    pub fn get_size(&self) -> Result<i64> {
104        Ok(self.t.call_method("getSize", ())?)
105    }
106
107    pub fn get_initial_size(&self) -> Result<i64> {
108        Ok(self.t.call_method("getInitialSize", ())?)
109    }
110
111    pub fn get_unit(&self, index: usize) -> Result<Unit<'lua>> {
112        Ok(self.t.call_method("getUnit", index)?)
113    }
114
115    pub fn get_units(&self) -> Result<Sequence<'lua, Unit<'lua>>> {
116        Ok(self.t.call_method("getUnits", ())?)
117    }
118
119    pub fn get_controller(&self) -> Result<Controller<'lua>> {
120        Ok(self.t.call_method("getController", ())?)
121    }
122
123    pub fn enable_emission(&self, on: bool) -> Result<()> {
124        Ok(self.t.call_method("enableEmission", on)?)
125    }
126
127    pub fn as_object(&self) -> Result<Object<'lua>> {
128        Ok(Object::from_lua(Value::Table(self.t.clone()), self.lua)?)
129    }
130}
131
132#[derive(Debug, Clone)]
133pub struct ClassGroup;
134
135impl<'lua> DcsObject<'lua> for Group<'lua> {
136    type Class = ClassGroup;
137
138    fn get_instance(lua: MizLua<'lua>, id: &DcsOid<Self::Class>) -> Result<Self> {
139        let t = lua.inner().create_table()?;
140        t.set_metatable(Some(lua.inner().globals().raw_get(&**id.class)?));
141        t.raw_set("id_", id.id)?;
142        let t = Group {
143            t,
144            lua: lua.inner(),
145        };
146        if !t.is_exist()? {
147            bail!("{} is an invalid group", id.id)
148        }
149        // work around for dcs bug that can cause isExist to return
150        // true even though the group is dead
151        if t.get_size()? == 0 {
152            bail!("{} is dead", id.id)
153        }
154        Ok(t)
155    }
156
157    fn get_instance_dyn<T>(lua: MizLua<'lua>, id: &DcsOid<T>) -> Result<Self> {
158        id.check_implements(lua, "Group")?;
159        let id = DcsOid {
160            id: id.id,
161            class: id.class.clone(),
162            t: PhantomData,
163        };
164        Self::get_instance(lua, &id)
165    }
166
167    fn change_instance(self, id: &DcsOid<Self::Class>) -> Result<Self> {
168        self.raw_set("id_", id.id)?;
169        if !self.is_exist()? {
170            bail!("{} is an invalid group", id.id)
171        }
172        // work around for dcs bug that can cause isExist to return
173        // true even though the group is dead
174        if self.get_size()? == 0 {
175            bail!("{} is dead", id.id)
176        }
177        Ok(self)
178    }
179
180    fn change_instance_dyn<T>(self, id: &DcsOid<T>) -> Result<Self> {
181        id.check_implements(MizLua(self.lua), "Group")?;
182        self.t.raw_set("id_", id.id)?;
183        if !self.is_exist()? {
184            bail!("{} is an invalid group", id.id)
185        }
186        // work around for dcs bug that can cause isExist to return
187        // true even though the group is dead
188        if self.get_size()? == 0 {
189            bail!("{} is dead", id.id)
190        }
191        Ok(self)
192    }
193}