dcso3/
event.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::{
15    as_tbl, as_tbl_ref, lua_err, object::Object, unit::Unit, weapon::Weapon, world::MarkPanel,
16    String, Time, value_to_json
17};
18use anyhow::{bail, Result};
19use log::{error, info};
20use mlua::{prelude::*, Value};
21use serde_derive::Serialize;
22
23#[derive(Debug, Clone, Serialize)]
24pub enum BirthPlace {
25    Air,
26    Runway,
27    Park,
28    HeliportHot,
29    HeliportCold,
30}
31
32#[derive(Debug, Clone, Serialize)]
33pub struct Shot<'lua> {
34    pub time: Time,
35    pub initiator: Unit<'lua>,
36    pub weapon: Weapon<'lua>,
37    pub weapon_name: String,
38}
39
40impl<'lua> FromLua<'lua> for Shot<'lua> {
41    fn from_lua(value: Value<'lua>, _: &'lua Lua) -> LuaResult<Self> {
42        let tbl = as_tbl("Shot", None, value).map_err(lua_err)?;
43        Ok(Self {
44            time: tbl.raw_get("time")?,
45            initiator: tbl.raw_get("initiator")?,
46            weapon: tbl.raw_get("weapon")?,
47            weapon_name: tbl.raw_get("weapon_name")?,
48        })
49    }
50}
51
52#[derive(Debug, Clone, Serialize)]
53pub struct ShootingEnd<'lua> {
54    pub time: Time,
55    pub initiator: Unit<'lua>,
56    pub weapon_name: String,
57}
58
59impl<'lua> FromLua<'lua> for ShootingEnd<'lua> {
60    fn from_lua(value: Value<'lua>, _: &'lua Lua) -> LuaResult<Self> {
61        let tbl = as_tbl("Shot", None, value).map_err(lua_err)?;
62        Ok(Self {
63            time: tbl.raw_get("time")?,
64            initiator: tbl.raw_get("initiator")?,
65            weapon_name: tbl.raw_get("weapon_name")?,
66        })
67    }
68}
69
70#[derive(Debug, Clone, Serialize)]
71pub struct WeaponUse<'lua> {
72    pub time: Time,
73    pub initiator: Option<Object<'lua>>,
74    pub target: Option<Object<'lua>>,
75    pub weapon_name: String,
76}
77
78impl<'lua> FromLua<'lua> for WeaponUse<'lua> {
79    fn from_lua(value: Value<'lua>, _: &'lua Lua) -> LuaResult<Self> {
80        let tbl = as_tbl("WeaponUse", None, value).map_err(lua_err)?;
81        Ok(Self {
82            time: tbl.raw_get("time")?,
83            initiator: tbl.raw_get("initiator")?,
84            target: tbl.raw_get("target")?,
85            weapon_name: tbl.raw_get("weapon_name")?,
86        })
87    }
88}
89
90#[derive(Debug, Clone, Serialize)]
91pub struct UnitEvent<'lua> {
92    pub time: Time,
93    pub initiator: Option<Object<'lua>>,
94}
95
96impl<'lua> FromLua<'lua> for UnitEvent<'lua> {
97    fn from_lua(value: Value<'lua>, _: &'lua Lua) -> LuaResult<Self> {
98        let tbl = as_tbl("UnitEvent", None, value).map_err(lua_err)?;
99        Ok(Self {
100            time: tbl.raw_get("time")?,
101            initiator: tbl.raw_get("initiator")?,
102        })
103    }
104}
105
106#[derive(Debug, Clone, Serialize)]
107pub struct EjectionEvent<'lua> {
108    pub time: Time,
109    pub initiator: Object<'lua>,
110    pub target: Object<'lua>,
111}
112
113impl<'lua> FromLua<'lua> for EjectionEvent<'lua> {
114    fn from_lua(value: Value<'lua>, _: &'lua Lua) -> LuaResult<Self> {
115        let tbl = as_tbl("EjectionEvent", None, value).map_err(lua_err)?;
116        Ok(Self {
117            time: tbl.raw_get("time")?,
118            initiator: tbl.raw_get("initiator")?,
119            target: tbl.raw_get("target")?,
120        })
121    }
122}
123
124#[derive(Debug, Clone, Serialize)]
125pub struct Birth<'lua> {
126    pub time: Time,
127    pub initiator: Object<'lua>,
128    pub place: Option<Object<'lua>>,
129    pub subplace: Option<i64>,
130}
131
132impl<'lua> FromLua<'lua> for Birth<'lua> {
133    fn from_lua(value: Value<'lua>, _: &'lua Lua) -> LuaResult<Self> {
134        let tbl = as_tbl("AtPlace", None, value).map_err(lua_err)?;
135        Ok(Self {
136            time: tbl.raw_get("time")?,
137            initiator: tbl.raw_get("initiator")?,
138            place: tbl.raw_get("place")?,
139            subplace: tbl.raw_get("subPlace")?,
140        })
141    }
142}
143
144#[derive(Debug, Clone, Serialize)]
145pub struct AtPlace<'lua> {
146    pub time: Time,
147    pub initiator: Object<'lua>,
148    pub place: Option<Object<'lua>>,
149    pub subplace: Option<i64>,
150}
151
152impl<'lua> FromLua<'lua> for AtPlace<'lua> {
153    fn from_lua(value: Value<'lua>, _: &'lua Lua) -> LuaResult<Self> {
154        let tbl = as_tbl("AtPlace", None, value).map_err(lua_err)?;
155        Ok(Self {
156            time: tbl.raw_get("time")?,
157            initiator: tbl.raw_get("initiator")?,
158            place: tbl.raw_get("place")?,
159            subplace: tbl.raw_get("subPlace")?,
160        })
161    }
162}
163
164#[derive(Debug, Clone, Serialize)]
165pub struct WeaponAdd<'lua> {
166    pub time: Time,
167    pub initiator: Object<'lua>,
168    pub weapon_name: String,
169}
170
171impl<'lua> FromLua<'lua> for WeaponAdd<'lua> {
172    fn from_lua(value: Value<'lua>, _lua: &'lua Lua) -> LuaResult<Self> {
173        let tbl = as_tbl("WeaponAdd", None, value).map_err(lua_err)?;
174        Ok(Self {
175            time: tbl.raw_get("time")?,
176            initiator: tbl.raw_get("initiator")?,
177            weapon_name: tbl.raw_get("weapon_name")?,
178        })
179    }
180}
181
182/// This is a dcs event
183#[derive(Debug, Clone, Serialize)]
184pub enum Event<'lua> {
185    Invalid,
186    Shot(Shot<'lua>),
187    Hit(WeaponUse<'lua>),
188    Takeoff(AtPlace<'lua>),
189    Land(AtPlace<'lua>),
190    Crash(UnitEvent<'lua>),
191    Ejection(EjectionEvent<'lua>),
192    Refueling,
193    Dead(UnitEvent<'lua>),
194    PilotDead(UnitEvent<'lua>),
195    BaseCaptured,
196    MissionStart,
197    MissionEnd,
198    TookControl,
199    RefuelingStop,
200    Birth(Birth<'lua>),
201    HumanFailure,
202    DetailedFailure,
203    EngineStartup(AtPlace<'lua>),
204    EngineShutdown(AtPlace<'lua>),
205    PlayerEnterUnit(UnitEvent<'lua>),
206    PlayerLeaveUnit(UnitEvent<'lua>),
207    PlayerComment,
208    ShootingStart(WeaponUse<'lua>),
209    ShootingEnd(ShootingEnd<'lua>),
210    MarkAdded(MarkPanel<'lua>),
211    MarkChange(MarkPanel<'lua>),
212    MarkRemoved(MarkPanel<'lua>),
213    Kill(WeaponUse<'lua>),
214    Score(UnitEvent<'lua>),
215    UnitLost(UnitEvent<'lua>),
216    LandingAfterEjection,
217    ParatrooperLanding,
218    DiscardChairAfterEjection,
219    WeaponAdd(WeaponAdd<'lua>),
220    TriggerZone,
221    LandingQualityMark,
222    Bda,
223    AiAbortMission(UnitEvent<'lua>),
224    DayNight,
225    FlightTime,
226    PlayerSelfKillPilot,
227    PlayerCaptureAirfield,
228    EmergencyLanding,
229    UnitCreateTask,
230    UnitDeleteTask,
231    SimulationStart,
232    WeaponRearm,
233    WeaponDrop,
234    UnitTaskTimeout,
235    UnitTaskStage,
236    MacSubtaskScore,
237    MacExtraScore,
238    MissionRestart,
239    MissionWinner,
240    PostponedTakeoff(AtPlace<'lua>),
241    PostponedLand(AtPlace<'lua>),
242    Max,
243}
244
245fn translate<'a, 'lua: 'a>(lua: &'lua Lua, id: i64, value: Value<'lua>) -> Result<Event<'lua>> {
246    Ok(match id {
247        0 => Event::Invalid,
248        1 => Event::Shot(Shot::from_lua(value, lua)?),
249        2 => Event::Hit(WeaponUse::from_lua(value, lua)?),
250        3 => Event::Takeoff(AtPlace::from_lua(value, lua)?),
251        4 => Event::Land(AtPlace::from_lua(value, lua)?),
252        5 => Event::Crash(UnitEvent::from_lua(value, lua)?),
253        6 => Event::Ejection(EjectionEvent::from_lua(value, lua)?),
254        7 => Event::Refueling,
255        8 => Event::Dead(UnitEvent::from_lua(value, lua)?),
256        9 => Event::PilotDead(UnitEvent::from_lua(value, lua)?),
257        10 => Event::BaseCaptured,
258        11 => Event::MissionStart,
259        12 => Event::MissionEnd,
260        13 => Event::TookControl,
261        14 => Event::RefuelingStop,
262        15 => Event::Birth(Birth::from_lua(value, lua)?),
263        16 => Event::HumanFailure,
264        17 => Event::DetailedFailure,
265        18 => Event::EngineStartup(AtPlace::from_lua(value, lua)?),
266        19 => Event::EngineShutdown(AtPlace::from_lua(value, lua)?),
267        20 => Event::PlayerEnterUnit(UnitEvent::from_lua(value, lua)?),
268        21 => Event::PlayerLeaveUnit(UnitEvent::from_lua(value, lua)?),
269        22 => Event::PlayerComment,
270        23 => Event::ShootingStart(WeaponUse::from_lua(value, lua)?),
271        24 => Event::ShootingEnd(ShootingEnd::from_lua(value, lua)?),
272        25 => Event::MarkAdded(MarkPanel::from_lua(value, lua)?),
273        26 => Event::MarkChange(MarkPanel::from_lua(value, lua)?),
274        27 => Event::MarkRemoved(MarkPanel::from_lua(value, lua)?),
275        28 => Event::Kill(WeaponUse::from_lua(value, lua)?),
276        29 => Event::Score(UnitEvent::from_lua(value, lua)?),
277        30 => Event::UnitLost(UnitEvent::from_lua(value, lua)?),
278        31 => Event::LandingAfterEjection,
279        32 => Event::ParatrooperLanding,
280        33 => Event::DiscardChairAfterEjection,
281        34 => Event::WeaponAdd(WeaponAdd::from_lua(value, lua)?),
282        35 => Event::TriggerZone,
283        36 => Event::LandingQualityMark,
284        37 => Event::Bda,
285        38 => Event::AiAbortMission(UnitEvent::from_lua(value, lua)?),
286        39 => Event::DayNight,
287        40 => Event::FlightTime,
288        41 => Event::PlayerSelfKillPilot,
289        42 => Event::PlayerCaptureAirfield,
290        43 => Event::EmergencyLanding,
291        44 => Event::UnitCreateTask,
292        45 => Event::UnitDeleteTask,
293        46 => Event::SimulationStart,
294        47 => Event::WeaponRearm,
295        48 => Event::WeaponDrop,
296        49 => Event::UnitTaskTimeout,
297        50 => Event::UnitTaskStage,
298        51 => Event::MacSubtaskScore,
299        52 => Event::MacExtraScore,
300        53 => Event::MissionRestart,
301        54 => {
302            info!("mission winner event {}", value_to_json(&value));
303            Event::MissionWinner
304        },
305        55 => Event::PostponedTakeoff(AtPlace::from_lua(value, lua)?),
306        56 => Event::PostponedLand(AtPlace::from_lua(value, lua)?),
307        57 => Event::Max,
308        n => bail!("unknown event {n}"),
309    })
310}
311
312impl<'lua> FromLua<'lua> for Event<'lua> {
313    fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> LuaResult<Self> {
314        let id = as_tbl_ref("Event", &value)
315            .map_err(lua_err)?
316            .raw_get("id")?;
317        match translate(lua, id, value.clone()) {
318            Ok(ev) => Ok(ev),
319            Err(e) => {
320                let s = value_to_json(&value);
321                error!("error translating event {id}: {e:?}, value: {s}");
322                Err(lua_err(e))
323            }
324        }
325    }
326}