1use 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}