Skip to main content

rustapi/actions/
window.rs

1use crate::prelude::*;
2use rusterix::{PixelSource, Value};
3
4pub struct Window {
5    id: TheId,
6    nodeui: TheNodeUI,
7}
8
9impl Action for Window {
10    fn new() -> Self
11    where
12        Self: Sized,
13    {
14        let mut nodeui: TheNodeUI = TheNodeUI::default();
15
16        nodeui.add_item(TheNodeUIItem::OpenTree("window".into()));
17        nodeui.add_item(TheNodeUIItem::FloatEditSlider(
18            "actionWindowInset".into(),
19            "".into(),
20            "".into(),
21            0.0,
22            -1.0..=1.0,
23            false,
24        ));
25        nodeui.add_item(TheNodeUIItem::FloatEditSlider(
26            "actionWindowFrameWidth".into(),
27            "".into(),
28            "".into(),
29            0.08,
30            0.01..=1.0,
31            false,
32        ));
33        nodeui.add_item(TheNodeUIItem::CloseTree);
34
35        nodeui.add_item(TheNodeUIItem::OpenTree("material".into()));
36        nodeui.add_item(TheNodeUIItem::Icons(
37            "actionMaterialFrameTile".into(),
38            "".into(),
39            "".into(),
40            vec![(
41                TheRGBABuffer::new(TheDim::sized(36, 36)),
42                "".to_string(),
43                Uuid::nil(),
44            )],
45        ));
46        nodeui.add_item(TheNodeUIItem::Text(
47            "actionMaterialFrameTileId".into(),
48            "".into(),
49            "".into(),
50            "".into(),
51            None,
52            false,
53        ));
54        nodeui.add_item(TheNodeUIItem::Icons(
55            "actionMaterialGlassTile".into(),
56            "".into(),
57            "".into(),
58            vec![(
59                TheRGBABuffer::new(TheDim::sized(36, 36)),
60                "".to_string(),
61                Uuid::nil(),
62            )],
63        ));
64        nodeui.add_item(TheNodeUIItem::Text(
65            "actionMaterialGlassTileId".into(),
66            "".into(),
67            "".into(),
68            "".into(),
69            None,
70            false,
71        ));
72        nodeui.add_item(TheNodeUIItem::CloseTree);
73
74        nodeui.add_item(TheNodeUIItem::Markdown("desc".into(), "".into()));
75
76        Self {
77            id: TheId::named(&fl!("action_window")),
78            nodeui,
79        }
80    }
81
82    fn id(&self) -> TheId {
83        self.id.clone()
84    }
85
86    fn info(&self) -> String {
87        fl!("action_window_desc")
88    }
89
90    fn role(&self) -> ActionRole {
91        ActionRole::Editor
92    }
93
94    fn is_applicable(&self, map: &Map, _ctx: &mut TheContext, server_ctx: &ServerContext) -> bool {
95        !map.selected_sectors.is_empty()
96            && server_ctx.editor_view_mode == EditorViewMode::D2
97            && server_ctx.editing_surface.is_some()
98    }
99
100    fn load_params(&mut self, map: &Map) {
101        if let Some(sector_id) = map.selected_sectors.first()
102            && let Some(sector) = map.find_sector(*sector_id)
103        {
104            self.nodeui.set_f32_value(
105                "actionWindowInset",
106                sector.properties.get_float_default("profile_inset", 0.0),
107            );
108            self.nodeui.set_f32_value(
109                "actionWindowFrameWidth",
110                sector
111                    .properties
112                    .get_float_default("window_frame_width", 0.08),
113            );
114        }
115    }
116
117    fn load_params_project(&mut self, project: &Project, server_ctx: &mut ServerContext) {
118        let mut frame_icon = TheRGBABuffer::new(TheDim::sized(36, 36));
119        let mut frame_id = Uuid::nil();
120        let mut frame_text = String::new();
121        let mut glass_icon = TheRGBABuffer::new(TheDim::sized(36, 36));
122        let mut glass_id = Uuid::nil();
123        let mut glass_text = String::new();
124
125        if let Some(map) = project.get_map(server_ctx)
126            && let Some(sector_id) = map.selected_sectors.first()
127            && let Some(sector) = map.find_sector(*sector_id)
128        {
129            if let Some(Value::Source(PixelSource::TileId(id))) =
130                sector.properties.get("window_frame_source")
131            {
132                if let Some(tile) = project.tiles.get(id)
133                    && !tile.is_empty()
134                {
135                    frame_icon = tile.textures[0].to_rgba();
136                    frame_id = *id;
137                    frame_text = id.to_string();
138                }
139            }
140            if let Some(Value::Source(PixelSource::TileId(id))) =
141                sector.properties.get("window_glass_source")
142            {
143                if let Some(tile) = project.tiles.get(id)
144                    && !tile.is_empty()
145                {
146                    glass_icon = tile.textures[0].to_rgba();
147                    glass_id = *id;
148                    glass_text = id.to_string();
149                }
150            }
151            if let Some(Value::Source(PixelSource::PaletteIndex(index))) =
152                sector.properties.get("window_frame_source")
153            {
154                frame_id = Uuid::nil();
155                frame_icon = TheRGBABuffer::new(TheDim::sized(36, 36));
156                frame_text = index.to_string();
157            }
158            if let Some(Value::Source(PixelSource::PaletteIndex(index))) =
159                sector.properties.get("window_glass_source")
160            {
161                glass_id = Uuid::nil();
162                glass_icon = TheRGBABuffer::new(TheDim::sized(36, 36));
163                glass_text = index.to_string();
164            }
165        }
166        self.nodeui
167            .set_text_value("actionMaterialFrameTileId", frame_text);
168        self.nodeui
169            .set_text_value("actionMaterialGlassTileId", glass_text);
170
171        if let Some(TheNodeUIItem::Icons(_, _, _, items)) =
172            self.nodeui.get_item_mut("actionMaterialFrameTile")
173            && items.len() == 1
174        {
175            items[0].0 = frame_icon;
176            items[0].2 = frame_id;
177        }
178
179        if let Some(TheNodeUIItem::Icons(_, _, _, items)) =
180            self.nodeui.get_item_mut("actionMaterialGlassTile")
181            && items.len() == 1
182        {
183            items[0].0 = glass_icon;
184            items[0].2 = glass_id;
185        }
186    }
187
188    fn apply(
189        &self,
190        map: &mut Map,
191        _ui: &mut TheUI,
192        _ctx: &mut TheContext,
193        server_ctx: &mut ServerContext,
194    ) -> Option<ProjectUndoAtom> {
195        let prev = map.clone();
196        let mut changed = false;
197
198        let inset = self
199            .nodeui
200            .get_f32_value("actionWindowInset")
201            .unwrap_or(0.0);
202        let frame_w = self
203            .nodeui
204            .get_f32_value("actionWindowFrameWidth")
205            .unwrap_or(0.08);
206
207        let frame_icon_id = self
208            .nodeui
209            .get_tile_id("actionMaterialFrameTile", 0)
210            .unwrap_or(Uuid::nil());
211        let glass_icon_id = self
212            .nodeui
213            .get_tile_id("actionMaterialGlassTile", 0)
214            .unwrap_or(Uuid::nil());
215
216        let frame_text = self
217            .nodeui
218            .get_text_value("actionMaterialFrameTileId")
219            .unwrap_or_default();
220        let glass_text = self
221            .nodeui
222            .get_text_value("actionMaterialGlassTileId")
223            .unwrap_or_default();
224
225        let parse_source = |text: &str, fallback: Uuid| -> Option<PixelSource> {
226            let t = text.trim();
227            if !t.is_empty() {
228                if let Ok(id) = Uuid::parse_str(t) {
229                    return Some(PixelSource::TileId(id));
230                }
231                if let Ok(idx) = t.parse::<u16>() {
232                    return Some(PixelSource::PaletteIndex(idx));
233                }
234            }
235            if fallback != Uuid::nil() {
236                Some(PixelSource::TileId(fallback))
237            } else {
238                None
239            }
240        };
241        let frame_source = parse_source(&frame_text, frame_icon_id);
242        let glass_source = parse_source(&glass_text, glass_icon_id);
243
244        for sector_id in &map.selected_sectors.clone() {
245            if let Some(sector) = map.find_sector_mut(*sector_id) {
246                sector.properties.set("profile_op", Value::Int(4));
247                sector.properties.set("profile_inset", Value::Float(inset));
248                sector
249                    .properties
250                    .set("window_frame_width", Value::Float(frame_w.max(0.01)));
251
252                if let Some(source) = frame_source.clone() {
253                    sector
254                        .properties
255                        .set("window_frame_source", Value::Source(source));
256                } else {
257                    sector.properties.remove("window_frame_source");
258                }
259
260                if let Some(source) = glass_source.clone() {
261                    sector
262                        .properties
263                        .set("window_glass_source", Value::Source(source));
264                } else {
265                    sector.properties.remove("window_glass_source");
266                }
267
268                changed = true;
269            }
270        }
271
272        if changed {
273            Some(ProjectUndoAtom::MapEdit(
274                server_ctx.pc,
275                Box::new(prev),
276                Box::new(map.clone()),
277            ))
278        } else {
279            None
280        }
281    }
282
283    fn params(&self) -> TheNodeUI {
284        self.nodeui.clone()
285    }
286
287    fn handle_event(
288        &mut self,
289        event: &TheEvent,
290        project: &mut Project,
291        _ui: &mut TheUI,
292        ctx: &mut TheContext,
293        _server_ctx: &mut ServerContext,
294    ) -> bool {
295        if let TheEvent::TileDropped(id, tile_id, index) = event
296            && let Some(item) = self.nodeui.get_item_mut(&id.name)
297            && let TheNodeUIItem::Icons(_, _, _, items) = item
298            && *index < items.len()
299            && let Some(tile) = project.tiles.get(tile_id)
300            && !tile.is_empty()
301        {
302            items[*index].0 = tile.textures[0].to_rgba();
303            items[*index].2 = *tile_id;
304
305            if id.name == "actionMaterialFrameTile" {
306                self.nodeui
307                    .set_text_value("actionMaterialFrameTileId", tile_id.to_string());
308            } else if id.name == "actionMaterialGlassTile" {
309                self.nodeui
310                    .set_text_value("actionMaterialGlassTileId", tile_id.to_string());
311            }
312
313            ctx.ui.send(TheEvent::Custom(
314                TheId::named("Update Action List"),
315                TheValue::Empty,
316            ));
317            return true;
318        }
319        self.nodeui.handle_event(event)
320    }
321}