dcso3/
mission_commands.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, coalition::Side, env::miz::GroupId, wrap_f, wrapped_table, LuaEnv, MizLua, String,
16};
17use anyhow::Result;
18use compact_str::format_compact;
19use mlua::{prelude::*, Value};
20use serde_derive::Serialize;
21use std::ops::Deref;
22
23#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
24struct ItemPath(Vec<String>);
25
26impl<'lua> IntoLua<'lua> for ItemPath {
27    fn into_lua(self, lua: &'lua Lua) -> LuaResult<Value<'lua>> {
28        let tbl = lua.create_table()?;
29        for s in self.0 {
30            tbl.raw_push(s)?
31        }
32        Ok(Value::Table(tbl))
33    }
34}
35
36impl<'lua> FromLua<'lua> for ItemPath {
37    fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> LuaResult<Self> {
38        let tbl = LuaTable::from_lua(value, lua)?;
39        let mut res = Vec::new();
40        for v in tbl.sequence_values() {
41            let v = v?;
42            res.push(String::from_lua(v, lua)?);
43        }
44        Ok(Self(res))
45    }
46}
47
48macro_rules! item {
49    ($name:ident) => {
50        #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
51        pub struct $name(ItemPath);
52
53        impl<'lua> IntoLua<'lua> for $name {
54            fn into_lua(self, lua: &'lua Lua) -> LuaResult<Value<'lua>> {
55                self.0.into_lua(lua)
56            }
57        }
58
59        impl<'lua> FromLua<'lua> for $name {
60            fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> LuaResult<Self> {
61                Ok(Self(ItemPath::from_lua(value, lua)?))
62            }
63        }
64
65        impl From<Vec<String>> for $name {
66            fn from(v: Vec<String>) -> Self {
67                Self(ItemPath(v))
68            }
69        }
70
71        impl Into<Vec<String>> for $name {
72            fn into(self) -> Vec<String> {
73                (self.0).0
74            }
75        }
76    };
77}
78
79item!(SubMenu);
80item!(CoalitionSubMenu);
81item!(GroupSubMenu);
82item!(CommandItem);
83item!(CoalitionCommandItem);
84item!(GroupCommandItem);
85
86wrapped_table!(MissionCommands, None);
87
88impl<'lua> MissionCommands<'lua> {
89    pub fn singleton(lua: MizLua<'lua>) -> Result<Self> {
90        Ok(lua.inner().globals().raw_get("missionCommands")?)
91    }
92
93    pub fn add_submenu(&self, name: String, parent: Option<SubMenu>) -> Result<SubMenu> {
94        Ok(self.call_function("addSubMenu", (name, parent))?)
95    }
96
97    pub fn add_command<F, A>(
98        &self,
99        name: String,
100        parent: Option<SubMenu>,
101        f: F,
102        arg: A,
103    ) -> Result<CommandItem>
104    where
105        F: Fn(MizLua, A) -> Result<()> + 'static,
106        A: IntoLua<'lua> + FromLua<'lua>,
107    {
108        let msg = format_compact!("command {:?},{name}", parent);
109        let f = self.lua.create_function(move |lua, arg: A| {
110            wrap_f(msg.as_str(), MizLua(lua), |lua| f(lua, arg))
111        })?;
112        Ok(self.call_function("addCommand", (name, parent, f, arg))?)
113    }
114
115    pub fn remove_submenu(&self, menu: SubMenu) -> Result<()> {
116        Ok(self.call_function("removeItem", menu)?)
117    }
118
119    pub fn remove_command(&self, item: CommandItem) -> Result<()> {
120        Ok(self.call_function("removeItem", item)?)
121    }
122
123    pub fn add_submenu_for_coalition(
124        &self,
125        side: Side,
126        name: String,
127        parent: Option<CoalitionSubMenu>,
128    ) -> Result<CoalitionSubMenu> {
129        Ok(self.call_function("addSubMenuForCoalition", (side, name, parent))?)
130    }
131
132    pub fn add_command_for_coalition<F, A>(
133        &self,
134        side: Side,
135        name: String,
136        parent: Option<CoalitionSubMenu>,
137        f: F,
138        arg: A,
139    ) -> Result<CoalitionCommandItem>
140    where
141        F: Fn(MizLua, A) -> Result<()> + 'static,
142        A: IntoLua<'lua> + FromLua<'lua>,
143    {
144        let msg = format_compact!("coa cmd {:?},{name}", parent);
145        let f = self.lua.create_function(move |lua, arg: A| {
146            wrap_f(msg.as_str(), MizLua(lua), |lua| f(lua, arg))
147        })?;
148        Ok(self.call_function("addCommandForCoalition", (side, name, parent, f, arg))?)
149    }
150
151    pub fn remove_submenu_for_coalition(&self, side: Side, menu: CoalitionSubMenu) -> Result<()> {
152        Ok(self.call_function("removeItemForCoalition", (side, menu))?)
153    }
154
155    pub fn remove_command_for_coalition(&self, side: Side, item: CoalitionCommandItem) -> Result<()> {
156        Ok(self.call_function("removeItemForCoalition", (side, item))?)
157    }
158
159    pub fn add_submenu_for_group(
160        &self,
161        group: GroupId,
162        name: String,
163        parent: Option<GroupSubMenu>,
164    ) -> Result<GroupSubMenu> {
165        Ok(self.call_function("addSubMenuForGroup", (group, name, parent))?)
166    }
167
168    pub fn add_command_for_group<F, A>(
169        &self,
170        group: GroupId,
171        name: String,
172        parent: Option<GroupSubMenu>,
173        f: F,
174        arg: A,
175    ) -> Result<GroupCommandItem>
176    where
177        F: Fn(MizLua, A) -> Result<()> + 'static,
178        A: IntoLua<'lua> + FromLua<'lua>,
179    {
180        let msg = format_compact!("grp cmd {:?}, {name}", parent);
181        let f = self.lua.create_function(move |lua, arg: A| {
182            wrap_f(msg.as_str(), MizLua(lua), |lua| f(lua, arg))
183        })?;
184        Ok(self.call_function("addCommandForGroup", (group, name, parent, f, arg))?)
185    }
186
187    pub fn remove_submenu_for_group(&self, group: GroupId, menu: GroupSubMenu) -> Result<()> {
188        Ok(self.call_function("removeItemForGroup", (group, menu))?)
189    }
190
191    pub fn remove_command_for_group(&self, group: GroupId, item: GroupCommandItem) -> Result<()> {
192        Ok(self.call_function("removeItemForGroup", (group, item))?)
193    }
194
195    pub fn clear_all_menus(&self) -> Result<()> {
196        Ok(self.call_function("removeItem", ())?)
197    }
198}