dcso3/
world.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, event::Event, unit::Unit, wrap_f, String};
15use crate::{
16    airbase::Airbase,
17    atomic_id, cvt_err,
18    env::miz::GroupId,
19    object::{Object, ObjectCategory},
20    trigger::{MarkId, SideFilter},
21    wrapped_table, LuaEnv, LuaVec3, MizLua, Position3, Sequence, Time,
22};
23use anyhow::Result;
24use compact_str::format_compact;
25use log::warn;
26use mlua::{prelude::*, Value};
27use serde_derive::{Deserialize, Serialize};
28use std::ops::Deref;
29
30#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
31pub enum SearchVolume {
32    Segment {
33        from: LuaVec3,
34        to: LuaVec3,
35    },
36    Box {
37        min: LuaVec3,
38        max: LuaVec3,
39    },
40    Sphere {
41        point: LuaVec3,
42        radius: f64,
43    },
44    Pyramid {
45        pos: Position3,
46        length: f32,
47        half_angle_hor: f32,
48        half_angle_ver: f32,
49    },
50}
51
52impl<'lua> IntoLua<'lua> for SearchVolume {
53    fn into_lua(self, lua: &'lua Lua) -> LuaResult<Value<'lua>> {
54        let tbl = lua.create_table()?;
55        let params = lua.create_table()?;
56        match self {
57            Self::Segment { from, to } => {
58                tbl.raw_set("id", 0)?;
59                params.raw_set("from", from)?;
60                params.raw_set("to", to)?;
61            }
62            Self::Box { min, max } => {
63                tbl.raw_set("id", 1)?;
64                params.raw_set("min", min)?;
65                params.raw_set("max", max)?;
66            }
67            Self::Sphere { point, radius } => {
68                tbl.raw_set("id", 2)?;
69                params.raw_set("point", point)?;
70                params.raw_set("radius", radius)?;
71            }
72            Self::Pyramid {
73                pos,
74                length,
75                half_angle_hor,
76                half_angle_ver,
77            } => {
78                tbl.raw_set("id", 3)?;
79                params.raw_set("pos", pos)?;
80                params.raw_set("length", length)?;
81                params.raw_set("halfAngleHor", half_angle_hor)?;
82                params.raw_set("halfAngleVer", half_angle_ver)?;
83            }
84        }
85        tbl.raw_set("params", params)?;
86        Ok(Value::Table(tbl))
87    }
88}
89
90#[derive(Debug, Clone, Serialize)]
91pub struct MarkPanel<'lua> {
92    pub id: MarkId,
93    pub time: Time,
94    pub initiator: Option<Unit<'lua>>,
95    pub side: SideFilter,
96    pub group_id: Option<GroupId>,
97    pub text: String,
98    pub pos: LuaVec3,
99}
100
101impl<'lua> FromLua<'lua> for MarkPanel<'lua> {
102    fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> LuaResult<Self> {
103        let tbl = LuaTable::from_lua(value, lua)?;
104        Ok(Self {
105            id: tbl.raw_get("idx")?,
106            time: tbl.raw_get("time")?,
107            initiator: tbl.raw_get("initiator")?,
108            side: match tbl.raw_get::<_, i64>("coalition")? {
109                -1 | 255 => SideFilter::All,
110                0 => SideFilter::Neutral,
111                1 => SideFilter::Red,
112                2 => SideFilter::Blue,
113                _ => return Err(cvt_err("side filter")),
114            },
115            group_id: match tbl.raw_get::<_, i64>("groupID")? {
116                -1 => None,
117                n => Some(GroupId::from(n)),
118            },
119            text: tbl.raw_get("text")?,
120            pos: tbl.raw_get("pos")?,
121        })
122    }
123}
124
125atomic_id!(HandlerId);
126
127impl HandlerId {
128    fn key(&self) -> String {
129        String(format_compact!("rustHandler{}", self.0))
130    }
131}
132
133wrapped_table!(World, None);
134
135impl<'lua> World<'lua> {
136    pub fn singleton(lua: MizLua<'lua>) -> Result<Self> {
137        Ok(lua.inner().globals().raw_get("world")?)
138    }
139
140    pub fn add_event_handler<F>(&self, f: F) -> Result<HandlerId>
141    where
142        F: Fn(MizLua<'lua>, Event) -> Result<()> + 'static,
143    {
144        let globals = self.lua.globals();
145        let id = HandlerId::new();
146        let tbl = self.lua.create_table()?;
147        tbl.set(
148            "onEvent",
149            self.lua
150                .create_function(move |lua, (_, ev): (Value, Value)| {
151                    match Event::from_lua(ev, lua) {
152                        Ok(ev) => wrap_f("event handler", MizLua(lua), |lua| f(lua, ev)),
153                        Err(e) => {
154                            warn!("error translating event: {:?}", e);
155                            Ok(())
156                        }
157                    }
158                })?,
159        )?;
160        self.t.call_function::<_, ()>("addEventHandler", tbl.clone())?;
161        globals.raw_set(id.key(), tbl)?;
162        Ok(id)
163    }
164
165    pub fn remove_event_handler(&self, id: HandlerId) -> Result<()> {
166        let globals = self.lua.globals();
167        let key = id.key();
168        let handler = globals.raw_get(key.clone())?;
169        let handler = as_tbl("EventHandler", None, handler)?;
170        self.t.call_function::<_, ()>("removeEventHandler", handler)?;
171        globals.raw_remove(key)?;
172        Ok(())
173    }
174
175    pub fn get_player(&self) -> Result<Sequence<'lua, Unit<'lua>>> {
176        Ok(self.t.call_function("getPlayer", ())?)
177    }
178
179    pub fn get_airbases(&self) -> Result<Sequence<'lua, Airbase<'lua>>> {
180        Ok(self.t.call_function("getAirbases", ())?)
181    }
182
183    pub fn search_objects<F, T>(
184        &self,
185        category: ObjectCategory,
186        volume: SearchVolume,
187        arg: T,
188        f: F,
189    ) -> Result<()>
190    where
191        T: IntoLua<'lua> + FromLua<'lua>,
192        F: Fn(MizLua, Object<'lua>, T) -> Result<bool> + 'static,
193    {
194        let f = self
195            .lua
196            .create_function(move |lua, (o, arg): (Object, T)| {
197                wrap_f("searchObjects", MizLua(lua), |lua| f(lua, o, arg))
198            })?;
199        Ok(self
200            .t
201            .call_function("searchObjects", (category, volume, f, arg))?)
202    }
203
204    pub fn remove_junk(&self, volume: SearchVolume) -> Result<i64> {
205        Ok(self.t.call_function("removeJunk", volume)?)
206    }
207
208    pub fn get_mark_panels(&self) -> Result<Sequence<'lua, MarkPanel<'lua>>> {
209        Ok(self.t.call_function("getMarkPanels", ())?)
210    }
211}