dcso3/
trigger.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 crate::{
15    as_tbl, atomic_id,
16    coalition::Side,
17    cvt_err,
18    env::miz::{Country, GroupId, UnitId},
19    simple_enum, wrapped_table, Color, LuaEnv, LuaVec3, MizLua, String,
20};
21use anyhow::Result;
22use mlua::{prelude::*, Value};
23use serde_derive::{Deserialize, Serialize};
24use std::ops::Deref;
25
26atomic_id!(MarkId);
27
28#[derive(Debug, Clone, Copy, PartialEq)]
29pub struct Zone {
30    pub point: LuaVec3,
31    pub radius: f64,
32}
33
34impl<'lua> FromLua<'lua> for Zone {
35    fn from_lua(value: Value<'lua>, _lua: &'lua Lua) -> LuaResult<Self> {
36        match value {
37            Value::Table(tbl) => Ok(Self {
38                point: tbl.raw_get("point")?,
39                radius: tbl.raw_get("radius")?,
40            }),
41            _ => Err(cvt_err("trigger::Zone")),
42        }
43    }
44}
45
46impl<'lua> IntoLua<'lua> for Zone {
47    fn into_lua(self, lua: &'lua Lua) -> LuaResult<Value<'lua>> {
48        let table = lua.create_table()?;
49        table.raw_set("point", self.point)?;
50        table.raw_set("radius", self.radius)?;
51        Ok(Value::Table(table))
52    }
53}
54
55simple_enum!(SmokeColor, u8, [
56    Green => 0,
57    Red => 1,
58    White => 2,
59    Orange => 3,
60    Blue => 4
61]);
62
63simple_enum!(SmokePreset, u8, [
64    SmallSmokeAndFire => 1,
65    MediumSmokeAndFire => 2,
66    LargeSmokeAndFire => 3,
67    HugeSmokeAndFire => 4,
68    SmallSmoke => 5,
69    MediumSmoke => 6,
70    LargeSmoke => 7,
71    HugeSmoke => 8
72]);
73
74simple_enum!(FlareColor, u8, [
75    Green => 0,
76    Red => 1,
77    White => 2,
78    Yellow => 3
79]);
80
81simple_enum!(Modulation, u8, [
82    AM => 0,
83    FM => 1
84]);
85
86simple_enum!(SideFilter, i8, [
87    All => -1,
88    Neutral => 0,
89    Red => 1,
90    Blue => 2
91]);
92
93impl SideFilter {
94    pub fn is_match(&self, side: &Side) -> bool {
95        match (self, side) {
96            (Self::All, _) => true,
97            (Self::Neutral, Side::Neutral) => true,
98            (Self::Blue, Side::Blue) => true,
99            (Self::Red, Side::Red) => true,
100            (_, _) => false
101        }
102    }
103}
104
105impl From<Side> for SideFilter {
106    fn from(value: Side) -> Self {
107        match value {
108            Side::Blue => SideFilter::Blue,
109            Side::Red => SideFilter::Red,
110            Side::Neutral => SideFilter::Neutral,
111        }
112    }
113}
114
115simple_enum!(LineType, u8, [
116    NoLine => 0,
117    Solid => 1,
118    Dashed => 2,
119    Dotted => 3,
120    DotDash => 4,
121    LongDash => 5,
122    TwoDash => 6
123]);
124
125#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
126pub struct LineSpec {
127    pub start: LuaVec3,
128    pub end: LuaVec3,
129    pub color: Color,
130    pub line_type: LineType,
131    pub read_only: bool,
132}
133
134#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
135pub struct CircleSpec {
136    pub center: LuaVec3,
137    pub radius: f64,
138    pub color: Color,
139    pub fill_color: Color,
140    pub line_type: LineType,
141    pub read_only: bool,
142}
143
144#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
145pub struct RectSpec {
146    pub start: LuaVec3,
147    pub end: LuaVec3,
148    pub color: Color,
149    pub fill_color: Color,
150    pub line_type: LineType,
151    pub read_only: bool,
152}
153
154#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
155pub struct QuadSpec {
156    pub p0: LuaVec3,
157    pub p1: LuaVec3,
158    pub p2: LuaVec3,
159    pub p3: LuaVec3,
160    pub color: Color,
161    pub fill_color: Color,
162    pub line_type: LineType,
163    pub read_only: bool,
164}
165
166#[derive(Debug, Clone, Serialize, Deserialize)]
167pub struct TextSpec {
168    pub pos: LuaVec3,
169    pub color: Color,
170    pub fill_color: Color,
171    pub font_size: u8,
172    pub read_only: bool,
173    pub text: String,
174}
175
176#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
177pub struct ArrowSpec {
178    pub start: LuaVec3,
179    pub end: LuaVec3,
180    pub color: Color,
181    pub fill_color: Color,
182    pub line_type: LineType,
183    pub read_only: bool,
184}
185
186wrapped_table!(Action, None);
187
188impl<'lua> Action<'lua> {
189    pub fn set_user_flag<K: IntoLua<'lua>, V: IntoLua<'lua>>(
190        &self,
191        key: K,
192        value: V,
193    ) -> Result<()> {
194        Ok(self.call_function("setUserFlag", (key, value))?)
195    }
196
197    pub fn set_ai_task(&self, group: GroupId, num: i64) -> Result<()> {
198        Ok(self.call_function("setAITask", (group, num))?)
199    }
200
201    pub fn explosion(&self, position: LuaVec3, power: f32) -> Result<()> {
202        Ok(self.call_function("explosion", (position, power))?)
203    }
204
205    pub fn smoke(&self, position: LuaVec3, color: SmokeColor) -> Result<()> {
206        Ok(self.call_function("smoke", (position, color))?)
207    }
208
209    pub fn effect_smoke_big(
210        &self,
211        position: LuaVec3,
212        preset: SmokePreset,
213        density: f32,
214        name: String,
215    ) -> Result<()> {
216        Ok(self.call_function("effectSmokeBig", (position, preset, density, name))?)
217    }
218
219    pub fn effect_smoke_stop(&self, name: String) -> Result<()> {
220        Ok(self.call_function("effectSmokeStop", name)?)
221    }
222
223    pub fn illumination_bomb(&self, position: LuaVec3, power: f32) -> Result<()> {
224        Ok(self.call_function("illuminationBomb", (position, power))?)
225    }
226
227    pub fn signal_flare(&self, position: LuaVec3, color: FlareColor, azimuth: u16) -> Result<()> {
228        Ok(self.call_function("signalFlare", (position, color, azimuth))?)
229    }
230
231    pub fn radio_transmission(
232        &self,
233        file: String,
234        origin: LuaVec3,
235        modulation: Modulation,
236        repeat: bool,
237        frequency: u64,
238        power: u64,
239        name: String,
240    ) -> Result<()> {
241        Ok(self.call_function(
242            "radioTransmission",
243            (file, origin, modulation, repeat, frequency, power, name),
244        )?)
245    }
246
247    pub fn stop_transmission(&self, name: String) -> Result<()> {
248        Ok(self.call_function("stopRadioTransmission", name)?)
249    }
250
251    pub fn set_unit_internal_cargo(&self, unit_name: String, mass: i64) -> Result<()> {
252        Ok(self.call_function("setUnitInternalCargo", (unit_name, mass))?)
253    }
254
255    pub fn out_sound(&self, file: String) -> Result<()> {
256        Ok(self.call_function("outSound", file)?)
257    }
258
259    pub fn out_sound_for_coalition(&self, side: Side, file: String) -> Result<()> {
260        Ok(self.call_function("outSoundForCoalition", (side, file))?)
261    }
262
263    pub fn out_sound_for_country(&self, country: Country, file: String) -> Result<()> {
264        Ok(self.call_function("outSoundForCountry", (country, file))?)
265    }
266
267    pub fn out_sound_for_group(&self, group: GroupId, file: String) -> Result<()> {
268        Ok(self.call_function("outSoundForGroup", (group, file))?)
269    }
270
271    pub fn out_sound_for_unit(&self, unit: UnitId, file: String) -> Result<()> {
272        Ok(self.call_function("outSoundForUnit", (unit, file))?)
273    }
274
275    pub fn out_text(&self, text: String, display_time: i64, clear_view: bool) -> Result<()> {
276        Ok(self.call_function("outText", (text, display_time, clear_view))?)
277    }
278
279    pub fn out_text_for_coalition(
280        &self,
281        side: Side,
282        text: String,
283        display_time: i64,
284        clear_view: bool,
285    ) -> Result<()> {
286        Ok(self.call_function(
287            "outTextForCoalition",
288            (side, text, display_time, clear_view),
289        )?)
290    }
291
292    pub fn out_text_for_country(
293        &self,
294        country: Country,
295        text: String,
296        display_time: i64,
297        clear_view: bool,
298    ) -> Result<()> {
299        Ok(self.call_function(
300            "outTextForCountry",
301            (country, text, display_time, clear_view),
302        )?)
303    }
304
305    pub fn out_text_for_group(
306        &self,
307        group: GroupId,
308        text: String,
309        display_time: i64,
310        clear_view: bool,
311    ) -> Result<()> {
312        Ok(self.call_function("outTextForGroup", (group, text, display_time, clear_view))?)
313    }
314
315    pub fn out_text_for_unit(
316        &self,
317        unit: UnitId,
318        text: String,
319        display_time: i64,
320        clear_view: bool,
321    ) -> Result<()> {
322        Ok(self.call_function("outTextForUnit", (unit, text, display_time, clear_view))?)
323    }
324
325    pub fn mark_to_all(
326        &self,
327        id: MarkId,
328        text: String,
329        position: LuaVec3,
330        read_only: bool,
331        message: Option<String>,
332    ) -> Result<()> {
333        Ok(self.call_function("markToAll", (id, text, position, read_only, message))?)
334    }
335
336    pub fn mark_to_coalition(
337        &self,
338        id: MarkId,
339        text: String,
340        position: LuaVec3,
341        side: Side,
342        read_only: bool,
343        message: Option<String>,
344    ) -> Result<()> {
345        Ok(self.call_function(
346            "markToCoalition",
347            (id, text, position, side, read_only, message),
348        )?)
349    }
350
351    pub fn mark_to_group(
352        &self,
353        id: MarkId,
354        text: String,
355        position: LuaVec3,
356        group: GroupId,
357        read_only: bool,
358        message: Option<String>,
359    ) -> Result<()> {
360        Ok(self.call_function(
361            "markToGroup",
362            (id, text, position, group, read_only, message),
363        )?)
364    }
365
366    pub fn remove_mark(&self, id: MarkId) -> Result<()> {
367        Ok(self.call_function("removeMark", id)?)
368    }
369
370    pub fn line_to_all(
371        &self,
372        side: SideFilter,
373        id: MarkId,
374        spec: LineSpec,
375        message: Option<String>,
376    ) -> Result<()> {
377        Ok(self.call_function(
378            "lineToAll",
379            (
380                side,
381                id,
382                spec.start,
383                spec.end,
384                spec.color,
385                spec.line_type,
386                spec.read_only,
387                message,
388            ),
389        )?)
390    }
391
392    pub fn circle_to_all(
393        &self,
394        side: SideFilter,
395        id: MarkId,
396        spec: CircleSpec,
397        message: Option<String>,
398    ) -> Result<()> {
399        Ok(self.call_function(
400            "circleToAll",
401            (
402                side,
403                id,
404                spec.center,
405                spec.radius,
406                spec.color,
407                spec.fill_color,
408                spec.line_type,
409                spec.read_only,
410                message,
411            ),
412        )?)
413    }
414
415    pub fn rect_to_all(
416        &self,
417        side: SideFilter,
418        id: MarkId,
419        spec: RectSpec,
420        message: Option<String>,
421    ) -> Result<()> {
422        Ok(self.call_function(
423            "rectToAll",
424            (
425                side,
426                id,
427                spec.start,
428                spec.end,
429                spec.color,
430                spec.fill_color,
431                spec.line_type,
432                spec.read_only,
433                message,
434            ),
435        )?)
436    }
437
438    pub fn quad_to_all(
439        &self,
440        side: SideFilter,
441        id: MarkId,
442        spec: QuadSpec,
443        message: Option<String>,
444    ) -> Result<()> {
445        Ok(self.call_function(
446            "quadToAll",
447            (
448                side,
449                id,
450                spec.p0,
451                spec.p1,
452                spec.p2,
453                spec.p3,
454                spec.color,
455                spec.fill_color,
456                spec.line_type,
457                spec.read_only,
458                message,
459            ),
460        )?)
461    }
462
463    pub fn text_to_all(&self, side: SideFilter, id: MarkId, spec: TextSpec) -> Result<()> {
464        Ok(self.call_function(
465            "textToAll",
466            (
467                side,
468                id,
469                spec.pos,
470                spec.color,
471                spec.fill_color,
472                spec.font_size,
473                spec.read_only,
474                spec.text,
475            ),
476        )?)
477    }
478
479    pub fn arrow_to_all(
480        &self,
481        side: SideFilter,
482        id: MarkId,
483        spec: ArrowSpec,
484        message: Option<String>,
485    ) -> Result<()> {
486        Ok(self.call_function(
487            "arrowToAll",
488            (
489                side,
490                id,
491                spec.start,
492                spec.end,
493                spec.color,
494                spec.fill_color,
495                spec.line_type,
496                spec.read_only,
497                message,
498            ),
499        )?)
500    }
501
502    pub fn set_markup_radius(&self, id: MarkId, radius: f64) -> Result<()> {
503        Ok(self.call_function("setMarkupRadius", (id, radius))?)
504    }
505
506    pub fn set_markup_text(&self, id: MarkId, text: String) -> Result<()> {
507        Ok(self.call_function("setMarkupText", (id, text))?)
508    }
509
510    pub fn set_markup_font_size(&self, id: MarkId, font_size: u8) -> Result<()> {
511        Ok(self.call_function("setMarkupFontSize", (id, font_size))?)
512    }
513
514    pub fn set_markup_color(&self, id: MarkId, color: Color) -> Result<()> {
515        Ok(self.call_function("setMarkupColor", (id, color))?)
516    }
517
518    pub fn set_markup_fill_color(&self, id: MarkId, fill_color: Color) -> Result<()> {
519        Ok(self.call_function("setMarkupColorFill", (id, fill_color))?)
520    }
521
522    pub fn set_markup_line_type(&self, id: MarkId, line_type: LineType) -> Result<()> {
523        Ok(self.call_function("setMarkupTypeLine", (id, line_type))?)
524    }
525
526    pub fn set_markup_position_end(&self, id: MarkId, pos: LuaVec3) -> Result<()> {
527        Ok(self.call_function("setMarkupPositionEnd", (id, pos))?)
528    }
529
530    pub fn set_markup_position_start(&self, id: MarkId, pos: LuaVec3) -> Result<()> {
531        Ok(self.call_function("setMarkupPositionStart", (id, pos))?)
532    }
533}
534
535wrapped_table!(Trigger, None);
536
537impl<'lua> Trigger<'lua> {
538    pub fn singleton(lua: MizLua<'lua>) -> Result<Self> {
539        Ok(lua.inner().globals().raw_get("trigger")?)
540    }
541
542    pub fn action(&self) -> Result<Action<'lua>> {
543        Ok(self.raw_get("action")?)
544    }
545
546    pub fn get_zone(&self, name: String) -> Result<Zone> {
547        let misc: LuaTable = self.t.raw_get("misc")?;
548        Ok(misc.call_function("getZone", name)?)
549    }
550}