Skip to main content

rustapi/actions/
extrude_sector.rs

1use crate::prelude::*;
2use rusterix::Surface;
3
4pub struct ExtrudeSector {
5    id: TheId,
6    nodeui: TheNodeUI,
7}
8
9impl Action for ExtrudeSector {
10    fn new() -> Self
11    where
12        Self: Sized,
13    {
14        let mut nodeui: TheNodeUI = TheNodeUI::default();
15
16        // Surface extrusion settings
17        nodeui.add_item(TheNodeUIItem::Checkbox(
18            "actionSurfEnable".into(),
19            "".into(),
20            "".into(),
21            true,
22        ));
23
24        let item = TheNodeUIItem::FloatEditSlider(
25            "actionDepth".into(),
26            "".into(),
27            "".into(),
28            0.2,
29            0.0..=1.0,
30            false,
31        );
32        nodeui.add_item(item);
33
34        nodeui.add_item(TheNodeUIItem::Checkbox(
35            "actionBackOpen".into(),
36            "".into(),
37            "".into(),
38            false,
39        ));
40
41        let item = TheNodeUIItem::Markdown("desc".into(), "".into());
42        nodeui.add_item(item);
43
44        Self {
45            id: TheId::named(&fl!("action_extrude_sector")),
46            nodeui,
47        }
48    }
49
50    fn id(&self) -> TheId {
51        self.id.clone()
52    }
53
54    fn info(&self) -> String {
55        fl!("action_extrude_sector_desc")
56    }
57
58    fn role(&self) -> ActionRole {
59        ActionRole::Editor
60    }
61
62    fn accel(&self) -> Option<TheAccelerator> {
63        Some(TheAccelerator::new(TheAcceleratorKey::ALT, 'e'))
64    }
65
66    fn is_applicable(&self, map: &Map, _ctx: &mut TheContext, server_ctx: &ServerContext) -> bool {
67        // 3D-only extrusion action.
68        if server_ctx.editor_view_mode == EditorViewMode::D2 {
69            return false;
70        }
71
72        !map.selected_sectors.is_empty() && map.selected_linedefs.is_empty()
73    }
74
75    fn apply(
76        &self,
77        map: &mut Map,
78        _ui: &mut TheUI,
79        _ctx: &mut TheContext,
80        server_ctx: &mut ServerContext,
81    ) -> Option<ProjectUndoAtom> {
82        let mut changed = false;
83        let prev = map.clone();
84
85        let distance = self.nodeui.get_f32_value("actionDepth").unwrap_or(2.0);
86        let surf_enable = self
87            .nodeui
88            .get_bool_value("actionSurfEnable")
89            .unwrap_or(true);
90        let back_open = self
91            .nodeui
92            .get_bool_value("actionBackOpen")
93            .unwrap_or(false);
94
95        // Apply to selected sectors: set/create surface extrusion settings
96        for sector_id in map.selected_sectors.clone() {
97            // Try to find an existing surface for this sector
98            let mut surface_id_opt: Option<Uuid> = None;
99            for (sid, s) in map.surfaces.iter() {
100                if s.sector_id == sector_id {
101                    surface_id_opt = Some(*sid);
102                    break;
103                }
104            }
105
106            // Create a new surface if needed
107            if surface_id_opt.is_none() {
108                if let Some(_sec) = map.find_sector(sector_id) {
109                    let mut surf = Surface::new(sector_id);
110                    surf.calculate_geometry(map);
111                    let id = surf.id;
112                    map.surfaces.insert(id, surf);
113                    surface_id_opt = Some(id);
114                }
115            }
116
117            if let Some(sid) = surface_id_opt {
118                if let Some(surf) = map.surfaces.get_mut(&sid) {
119                    // Distance directly sets depth; sign controls direction
120                    surf.extrusion.enabled = surf_enable;
121                    surf.extrusion.depth = distance;
122                    surf.extrusion.cap_front = true; // always cap front
123                    surf.extrusion.cap_back = !back_open; // optional back cap
124                    surf.extrusion.flip_normal = false; // not exposed; depth sign handles direction
125                    changed = true;
126                }
127            }
128        }
129
130        if changed {
131            Some(ProjectUndoAtom::MapEdit(
132                server_ctx.pc,
133                Box::new(prev),
134                Box::new(map.clone()),
135            ))
136        } else {
137            None
138        }
139    }
140
141    fn params(&self) -> TheNodeUI {
142        self.nodeui.clone()
143    }
144
145    fn handle_event(
146        &mut self,
147        event: &TheEvent,
148        _project: &mut Project,
149        _ui: &mut TheUI,
150        _ctx: &mut TheContext,
151        _server_ctx: &mut ServerContext,
152    ) -> bool {
153        self.nodeui.handle_event(event)
154    }
155}