Skip to main content

rustapi/actions/
relief.rs

1use crate::prelude::*;
2use rusterix::{PixelSource, Value};
3
4pub struct Relief {
5    id: TheId,
6    nodeui: TheNodeUI,
7}
8
9impl Action for Relief {
10    fn new() -> Self
11    where
12        Self: Sized,
13    {
14        let mut nodeui: TheNodeUI = TheNodeUI::default();
15
16        let item = TheNodeUIItem::FloatEditSlider(
17            "actionReliefHeight".into(),
18            "".into(),
19            "".into(),
20            0.2,       // default
21            0.0..=1.0, // range
22            false,
23        );
24        nodeui.add_item(item);
25
26        let item = TheNodeUIItem::Selector(
27            "actionReliefTarget".into(),
28            "".into(),
29            "".into(),
30            vec!["Front".to_string(), "Back".to_string()],
31            1,
32        );
33        nodeui.add_item(item);
34
35        let item = TheNodeUIItem::Icons(
36            "actionReliefTiles".into(),
37            "".into(),
38            "".into(),
39            vec![
40                (
41                    TheRGBABuffer::new(TheDim::sized(36, 36)),
42                    "CAP".to_string(),
43                    Uuid::nil(),
44                ),
45                (
46                    TheRGBABuffer::new(TheDim::sized(36, 36)),
47                    "SIDE".to_string(),
48                    Uuid::nil(),
49                ),
50            ],
51        );
52        nodeui.add_item(item);
53
54        let item = TheNodeUIItem::Markdown("desc".into(), "".into());
55        nodeui.add_item(item);
56
57        Self {
58            id: TheId::named(&fl!("action_relief")),
59            nodeui,
60        }
61    }
62
63    fn id(&self) -> TheId {
64        self.id.clone()
65    }
66
67    fn info(&self) -> String {
68        fl!("action_relief_desc")
69    }
70
71    fn role(&self) -> ActionRole {
72        ActionRole::Editor
73    }
74
75    fn accel(&self) -> Option<TheAccelerator> {
76        // Alt+E (Emboss/Relief). Change if you have a conflict.
77        Some(TheAccelerator::new(TheAcceleratorKey::ALT, 'e'))
78    }
79
80    fn is_applicable(&self, map: &Map, _ctx: &mut TheContext, server_ctx: &ServerContext) -> bool {
81        // Only in 2D Profile Editing Mode with sector selection
82        !map.selected_sectors.is_empty()
83            && server_ctx.editor_view_mode == EditorViewMode::D2
84            && server_ctx.editing_surface.is_some()
85    }
86
87    fn load_params(&mut self, map: &Map) {
88        if let Some(sector_id) = map.selected_sectors.first() {
89            if let Some(sector) = map.find_sector(*sector_id) {
90                self.nodeui.set_f32_value(
91                    "actionReliefHeight",
92                    sector.properties.get_float_default("profile_amount", 0.1),
93                );
94                self.nodeui.set_i32_value(
95                    "actionReliefTarget",
96                    sector.properties.get_int_default("profile_target", 1),
97                );
98            }
99        }
100    }
101
102    fn load_params_project(&mut self, project: &Project, server_ctx: &mut ServerContext) {
103        let mut cap_icon = TheRGBABuffer::new(TheDim::sized(36, 36));
104        let mut jamb_icon = TheRGBABuffer::new(TheDim::sized(36, 36));
105        let mut cap_id = Uuid::nil();
106        let mut jamb_id = Uuid::nil();
107
108        if let Some(map) = project.get_map(server_ctx) {
109            if let Some(sector_id) = map.selected_sectors.first() {
110                if let Some(sector) = map.find_sector(*sector_id) {
111                    if let Some(Value::Source(PixelSource::TileId(id))) =
112                        sector.properties.get("cap_source")
113                    {
114                        if let Some(tile) = project.tiles.get(id)
115                            && !tile.is_empty()
116                        {
117                            cap_icon = tile.textures[0].to_rgba();
118                            cap_id = *id;
119                        }
120                    }
121                    if let Some(Value::Source(PixelSource::TileId(id))) =
122                        sector.properties.get("jamb_source")
123                    {
124                        if let Some(tile) = project.tiles.get(id)
125                            && !tile.is_empty()
126                        {
127                            jamb_icon = tile.textures[0].to_rgba();
128                            jamb_id = *id;
129                        }
130                    }
131                }
132            }
133        }
134
135        if let Some(item) = self.nodeui.get_item_mut("actionReliefTiles") {
136            match item {
137                TheNodeUIItem::Icons(_, _, _, items) => {
138                    if items.len() == 2 {
139                        items[0].0 = cap_icon;
140                        items[0].2 = cap_id;
141                        items[1].0 = jamb_icon;
142                        items[1].2 = jamb_id;
143                    }
144                }
145                _ => {}
146            }
147        }
148    }
149
150    fn apply(
151        &self,
152        map: &mut Map,
153        _ui: &mut TheUI,
154        _ctx: &mut TheContext,
155        server_ctx: &mut ServerContext,
156    ) -> Option<ProjectUndoAtom> {
157        let mut changed = false;
158        let prev = map.clone();
159
160        let mut height = self
161            .nodeui
162            .get_f32_value("actionReliefHeight")
163            .unwrap_or(0.0);
164        if height < 0.0 {
165            height = 0.0;
166        }
167
168        let target = self.nodeui.get_i32_value("actionReliefTarget").unwrap_or(1);
169        let cap = self.nodeui.get_tile_id("actionReliefTiles", 0);
170        let jamb = self.nodeui.get_tile_id("actionReliefTiles", 1);
171
172        for sector_id in &map.selected_sectors.clone() {
173            if let Some(sector) = map.find_sector_mut(*sector_id) {
174                sector.properties.set("profile_op", Value::Int(1)); // Relief
175                sector
176                    .properties
177                    .set("profile_amount", Value::Float(height));
178
179                sector.properties.set("profile_target", Value::Int(target));
180
181                if let Some(cap) = cap
182                    && cap != Uuid::nil()
183                {
184                    sector.properties.set(
185                        "cap_source",
186                        Value::Source(rusterix::PixelSource::TileId(cap)),
187                    );
188                }
189                if let Some(jamb) = jamb
190                    && jamb != Uuid::nil()
191                {
192                    sector.properties.set(
193                        "jamb_source",
194                        Value::Source(rusterix::PixelSource::TileId(jamb)),
195                    );
196                }
197                changed = true;
198            }
199        }
200
201        if changed {
202            Some(ProjectUndoAtom::MapEdit(
203                server_ctx.pc,
204                Box::new(prev),
205                Box::new(map.clone()),
206            ))
207        } else {
208            None
209        }
210    }
211
212    fn params(&self) -> TheNodeUI {
213        self.nodeui.clone()
214    }
215
216    fn handle_event(
217        &mut self,
218        event: &TheEvent,
219        project: &mut Project,
220        _ui: &mut TheUI,
221        ctx: &mut TheContext,
222        _server_ctx: &mut ServerContext,
223    ) -> bool {
224        if let TheEvent::TileDropped(id, tile_id, index) = event {
225            if let Some(item) = self.nodeui.get_item_mut(&id.name) {
226                match item {
227                    TheNodeUIItem::Icons(_, _, _, items) => {
228                        if *index < items.len() {
229                            if let Some(tile) = project.tiles.get(tile_id)
230                                && !tile.is_empty()
231                            {
232                                items[*index].0 = tile.textures[0].to_rgba();
233                                items[*index].2 = *tile_id;
234                                ctx.ui.send(TheEvent::Custom(
235                                    TheId::named("Update Action List"),
236                                    TheValue::Empty,
237                                ));
238                                return true;
239                            }
240                        }
241                    }
242                    _ => {}
243                }
244            }
245        }
246        self.nodeui.handle_event(event)
247    }
248}