Skip to main content

rustapi/
mapeditor.rs

1use crate::editor::{EDITCAMERA, RUSTERIX, SIDEBARMODE};
2use crate::prelude::*;
3use rusterix::{D3Camera, Value};
4use vek::Vec2;
5
6pub struct MapEditor {
7    curr_tile_uuid: Option<Uuid>,
8
9    icon_normal_border_color: RGBA,
10    icon_selected_border_color: RGBA,
11}
12
13#[allow(clippy::new_without_default)]
14impl MapEditor {
15    pub fn new() -> Self {
16        Self {
17            curr_tile_uuid: None,
18
19            icon_normal_border_color: [100, 100, 100, 255],
20            icon_selected_border_color: [255, 255, 255, 255],
21        }
22    }
23
24    pub fn init_ui(
25        &mut self,
26        _ui: &mut TheUI,
27        _ctx: &mut TheContext,
28        _project: &mut Project,
29    ) -> TheCanvas {
30        let mut center = TheCanvas::new();
31
32        // let mut shared_layout = TheSharedHLayout::new(TheId::named("Editor Shared"));
33
34        // let mut region_editor = TheRGBALayout::new(TheId::named("Region Editor"));
35        // if let Some(rgba_view) = region_editor.rgba_view_mut().as_rgba_view() {
36        //     rgba_view.set_mode(TheRGBAViewMode::Display);
37
38        //     if let Some(buffer) = ctx.ui.icon("eldiron_map") {
39        //         rgba_view.set_buffer(buffer.clone());
40        //     }
41
42        //     rgba_view.set_grid_color([255, 255, 255, 5]);
43        //     rgba_view.set_hover_color(Some([255, 255, 255, 100]));
44        //     rgba_view.set_wheel_scale(-0.2);
45        // }
46
47        // let mut region_editor_canvas = TheCanvas::new();
48        // region_editor_canvas.set_layout(region_editor);
49        // shared_layout.add_canvas(region_editor_canvas);
50
51        // let mut render_canvas: TheCanvas = TheCanvas::new();
52        // let render_view = TheRenderView::new(TheId::named("RenderView"));
53        // render_canvas.set_widget(render_view);
54        // shared_layout.add_canvas(render_canvas);
55
56        //center.set_layout(shared_layout);
57
58        let mut poly_canvas: TheCanvas = TheCanvas::new();
59        let mut render_view = TheRenderView::new(TheId::named("PolyView"));
60        render_view.set_auto_focus(true);
61        poly_canvas.set_widget(render_view);
62
63        center.set_center(poly_canvas);
64
65        // Picker
66
67        let mut tile_picker = TheCanvas::new();
68        let mut vlayout = TheVLayout::new(TheId::named("Editor Icon Layout"));
69        vlayout.set_background_color(Some(TheThemeColors::ListLayoutBackground));
70        vlayout.limiter_mut().set_max_width(90);
71        vlayout.set_margin(Vec4::new(0, 10, 0, 5));
72
73        let mut icon_preview = TheIconView::new(TheId::named("Icon Preview"));
74        icon_preview.set_alpha_mode(false);
75        icon_preview.limiter_mut().set_max_size(Vec2::new(65, 65));
76        icon_preview.set_border_color(Some([100, 100, 100, 255]));
77        vlayout.add_widget(Box::new(icon_preview));
78
79        let mut spacer = TheIconView::new(TheId::empty());
80        spacer.limiter_mut().set_max_height(2);
81        vlayout.add_widget(Box::new(spacer));
82
83        let mut view_mode_gb = TheGroupButton::new(TheId::named("Map Editor Camera"));
84        view_mode_gb.add_text_status_icon(
85            "".to_string(),
86            "2D Camera".to_string(),
87            "square".to_string(),
88        );
89        view_mode_gb.add_text_status_icon(
90            "".to_string(),
91            "3D Camera: Iso".to_string(),
92            "cube".to_string(),
93        );
94        view_mode_gb.add_text_status_icon(
95            "".to_string(),
96            "3D Camera: First person".to_string(),
97            "camera".to_string(),
98        );
99        view_mode_gb.set_item_width(26);
100        vlayout.add_widget(Box::new(view_mode_gb));
101
102        let mut spacer = TheIconView::new(TheId::empty());
103        spacer.limiter_mut().set_max_height(0);
104        vlayout.add_widget(Box::new(spacer));
105
106        let mut grid_sub_div = TheTextLineEdit::new(TheId::named("Grid Subdiv"));
107        grid_sub_div.set_value(TheValue::Float(1.0));
108        // opacity.set_default_value(TheValue::Float(1.0));
109        grid_sub_div.set_info_text(Some("Subdiv".to_string()));
110        grid_sub_div.set_range(TheValue::RangeI32(1..=10));
111        grid_sub_div.set_continuous(true);
112        grid_sub_div.limiter_mut().set_max_width(150);
113        grid_sub_div.set_status_text(&fl!("status_map_editor_grid_sub_div"));
114        grid_sub_div.limiter_mut().set_max_width(75);
115        vlayout.add_widget(Box::new(grid_sub_div));
116
117        let mut spacer = TheIconView::new(TheId::empty());
118        spacer.limiter_mut().set_max_height(2);
119        vlayout.add_widget(Box::new(spacer));
120
121        let mut ground_icon = TheIconView::new(TheId::named("Ground Icon"));
122        ground_icon.set_text(Some("FLOOR".to_string()));
123        ground_icon.set_text_size(10.0);
124        ground_icon.set_text_color([200, 200, 200, 255]);
125        ground_icon.limiter_mut().set_max_size(Vec2::new(48, 48));
126        ground_icon.set_border_color(Some(self.icon_selected_border_color));
127
128        let mut wall_icon = TheIconView::new(TheId::named("Wall Icon"));
129        wall_icon.set_text(Some("WALL".to_string()));
130        wall_icon.set_text_size(10.0);
131        wall_icon.set_text_color([200, 200, 200, 255]);
132        wall_icon.limiter_mut().set_max_size(Vec2::new(48, 48));
133        wall_icon.set_border_color(Some(self.icon_normal_border_color));
134
135        let mut ceiling_icon = TheIconView::new(TheId::named("Ceiling Icon"));
136        ceiling_icon.set_text(Some("CEILING".to_string()));
137        ceiling_icon.set_text_size(10.0);
138        ceiling_icon.set_text_color([200, 200, 200, 255]);
139        ceiling_icon.limiter_mut().set_max_size(Vec2::new(48, 48));
140        ceiling_icon.set_border_color(Some(self.icon_normal_border_color));
141
142        // let mut cc_icon = TheIconView::new(TheId::named("Tile FX Icon"));
143        // cc_icon.set_text(Some("FX".to_string()));
144        // cc_icon.set_text_size(10.0);
145        // cc_icon.set_text_color([200, 200, 200, 255]);
146        // cc_icon.limiter_mut().set_max_size(vec2i(48, 48));
147        // cc_icon.set_border_color(Some(self.icon_normal_border_color));
148
149        vlayout.add_widget(Box::new(ground_icon));
150        vlayout.add_widget(Box::new(wall_icon));
151        vlayout.add_widget(Box::new(ceiling_icon));
152        //vlayout.add_widget(Box::new(cc_icon));
153
154        let mut spacer = TheIconView::new(TheId::empty());
155        spacer.limiter_mut().set_max_height(2);
156        vlayout.add_widget(Box::new(spacer));
157
158        let mut text = TheText::new(TheId::named("Cursor Position"));
159        text.set_text("()".to_string());
160        text.set_text_color([200, 200, 200, 255]);
161        vlayout.add_widget(Box::new(text));
162
163        // let mut text = TheText::new(TheId::named("Cursor Height"));
164        // text.set_text("H: -".to_string());
165        // text.set_text_color([200, 200, 200, 255]);
166        // vlayout.add_widget(Box::new(text));
167
168        tile_picker.set_layout(vlayout);
169        //center.set_left(tile_picker);
170
171        // Tool Params
172        // let mut toolbar_hlayout = TheHLayout::new(TheId::named("Game Tool Params"));
173        // toolbar_hlayout.set_background_color(None);
174        // toolbar_hlayout.set_margin(Vec4::new(10, 2, 5, 2));
175
176        // let mut toolbar_canvas = TheCanvas::default();
177        // toolbar_canvas.set_widget(TheTraybar::new(TheId::empty()));
178        // toolbar_canvas.set_layout(toolbar_hlayout);
179
180        center.bottom_is_expanding = true;
181        // center.set_bottom(toolbar_canvas);
182
183        center
184    }
185
186    pub fn load_from_project(&mut self, _ui: &mut TheUI, _ctx: &mut TheContext, project: &Project) {
187        RUSTERIX
188            .write()
189            .unwrap()
190            .set_tiles(project.tiles.clone(), true);
191    }
192
193    #[allow(clippy::suspicious_else_formatting)]
194    pub fn handle_event(
195        &mut self,
196        event: &TheEvent,
197        ui: &mut TheUI,
198        ctx: &mut TheContext,
199        project: &mut Project,
200        server_ctx: &mut ServerContext,
201    ) -> bool {
202        let mut redraw = false;
203
204        match event {
205            TheEvent::KeyCodeDown(TheValue::KeyCode(key)) => {
206                if !ui.focus_widget_supports_text_input(ctx) {
207                    match key {
208                        TheKeyCode::Up => {
209                            if server_ctx.editor_view_mode == EditorViewMode::FirstP {
210                                EDITCAMERA.write().unwrap().move_action =
211                                    Some(CustomMoveAction::Forward);
212                            }
213                        }
214                        TheKeyCode::Down => {
215                            if server_ctx.editor_view_mode == EditorViewMode::FirstP {
216                                EDITCAMERA.write().unwrap().move_action =
217                                    Some(CustomMoveAction::Backward);
218                            }
219                        }
220                        TheKeyCode::Left => {
221                            if server_ctx.editor_view_mode == EditorViewMode::FirstP {
222                                EDITCAMERA.write().unwrap().move_action =
223                                    Some(CustomMoveAction::Left);
224                            }
225                        }
226                        TheKeyCode::Right => {
227                            if server_ctx.editor_view_mode == EditorViewMode::FirstP {
228                                EDITCAMERA.write().unwrap().move_action =
229                                    Some(CustomMoveAction::Right);
230                            }
231                        }
232                        _ => {}
233                    }
234                }
235            }
236            TheEvent::KeyCodeUp(TheValue::KeyCode(_)) => {
237                if server_ctx.editor_view_mode == EditorViewMode::FirstP {
238                    EDITCAMERA.write().unwrap().move_action = None;
239                }
240            }
241
242            TheEvent::Copy => {
243                if !server_ctx.polyview_has_focus(ctx) {
244                    return false;
245                }
246                if let Some(map) = project.get_map_mut(server_ctx) {
247                    if map.has_selection() {
248                        server_ctx.clipboard = map.copy_selected(false);
249                        ctx.ui.send(TheEvent::SetStatusText(
250                            TheId::empty(),
251                            "Geometry copied into clipboard.".to_string(),
252                        ));
253                    } else {
254                        ctx.ui.send(TheEvent::SetStatusText(
255                            TheId::empty(),
256                            "No geometry selected!".to_string(),
257                        ));
258                    }
259                }
260            }
261            TheEvent::Cut => {
262                if !server_ctx.polyview_has_focus(ctx) {
263                    return false;
264                }
265                if let Some(map) = project.get_map_mut(server_ctx) {
266                    if map.has_selection() {
267                        let _prev = map.clone();
268                        server_ctx.clipboard = map.copy_selected(true);
269                        ctx.ui.send(TheEvent::SetStatusText(
270                            TheId::empty(),
271                            "Geometry copied into clipboard.".to_string(),
272                        ));
273                    } else {
274                        ctx.ui.send(TheEvent::SetStatusText(
275                            TheId::empty(),
276                            "No geometry selected!".to_string(),
277                        ));
278                    }
279                }
280            }
281            TheEvent::Paste(_, _) => {
282                // TODO use focus_widget_supports_clipboard here
283                if !server_ctx.clipboard.is_empty() && server_ctx.polyview_has_focus(ctx) {
284                    ctx.ui.send(TheEvent::SetStatusText(
285                        TheId::empty(),
286                        "Geometry pasted. Click to insert, Escape to cancel.".to_string(),
287                    ));
288                    server_ctx.paste_clipboard = Some(server_ctx.clipboard.clone());
289                }
290            }
291            TheEvent::Custom(id, value) => {
292                if id.name == "Base State Selected" {
293                    if let Some(layout) = ui.get_text_layout("Node Settings") {
294                        layout.clear();
295                    }
296                } else if id.name == "SoftRig Selected" {
297                    if let TheValue::Id(id) = value {
298                        let mut nodeui: TheNodeUI = TheNodeUI::default();
299
300                        if let Some(map) = project.get_map(server_ctx) {
301                            let name = if let Some(softrig) = map.softrigs.get(id) {
302                                softrig.name.clone()
303                            } else {
304                                "???".to_string()
305                            };
306
307                            let item = TheNodeUIItem::Text(
308                                "softRigName".into(),
309                                "Rig Name".into(),
310                                "Set the name of the soft rig keyframe.".into(),
311                                name,
312                                None,
313                                false,
314                            );
315                            nodeui.add_item(item);
316                        }
317
318                        if let Some(layout) = ui.get_text_layout("Node Settings") {
319                            nodeui.apply_to_text_layout(layout);
320                            ctx.ui.relayout = true;
321
322                            ctx.ui.send(TheEvent::Custom(
323                                TheId::named("Show Node Settings"),
324                                TheValue::Text("Soft Rig Settings".to_string()),
325                            ));
326                        }
327                    }
328                } else if id.name == "Map Selection Changed" {
329                    set_code(ui, ctx, project, server_ctx);
330                    self.apply_map_settings(ui, ctx, project, server_ctx);
331
332                    ctx.ui.send(TheEvent::Custom(
333                        TheId::named("Update Action List"),
334                        TheValue::Empty,
335                    ));
336
337                    ctx.ui.send(TheEvent::Custom(
338                        TheId::named("Update Action Parameters"),
339                        TheValue::Empty,
340                    ));
341                }
342                // else if id.name == "Cursor Pos Changed" {
343                //     if let Some(text) = ui.get_text("Cursor Position") {
344                //         if let Some(v) = value.to_vec2f() {
345                //             text.set_text(format!("{}, {}", v.x, v.y));
346                //         }
347                //         redraw = true;
348                //     }
349
350                //     if let Some(layout) = ui.get_layout("Editor Icon Layout") {
351                //         layout.relayout(ctx);
352                //     }
353                // }
354                //crate::editor::RUSTERIX.write().unwrap().set_dirty();
355            }
356            TheEvent::RenderViewScrollBy(id, coord) => {
357                if id.name == "PolyView" {
358                    let is_running = crate::editor::RUSTERIX.read().unwrap().server.state
359                        == rusterix::ServerState::Running;
360                    if is_running && server_ctx.game_mode {
361                        let mut rusterix = crate::editor::RUSTERIX.write().unwrap();
362                        rusterix.client.target_offset -= *coord;
363                    } else if !server_ctx.world_mode {
364                        if server_ctx.editor_view_mode == EditorViewMode::D2
365                            && let Some(map) = project.get_map_mut(server_ctx)
366                        {
367                            if ui.ctrl || ui.logo {
368                                let old_grid_size = map.grid_size;
369
370                                map.grid_size += coord.y as f32;
371                                map.grid_size = map.grid_size.clamp(5.0, 100.0);
372
373                                let scale = map.grid_size / old_grid_size;
374                                map.offset *= scale;
375                            } else {
376                                map.offset += Vec2::new(-coord.x as f32, coord.y as f32);
377                            }
378                            map.curr_rectangle = None;
379                        }
380
381                        if server_ctx.get_map_context() == MapContext::Region {
382                            if server_ctx.editor_view_mode == EditorViewMode::D2
383                                && server_ctx.profile_view.is_some()
384                            {
385                            } else {
386                                if server_ctx.editor_view_mode != EditorViewMode::D2 {
387                                    if ui.logo || ui.ctrl {
388                                        EDITCAMERA
389                                            .write()
390                                            .unwrap()
391                                            .scroll_by(coord.y as f32, server_ctx);
392                                    } else if ui.alt {
393                                        EDITCAMERA
394                                            .write()
395                                            .unwrap()
396                                            .rotate(coord.map(|v| v as f32), server_ctx);
397                                    } else if let Some(region) =
398                                        project.get_region_mut(&server_ctx.curr_region)
399                                    {
400                                        // Move camera
401                                        if server_ctx.editor_view_mode == EditorViewMode::Orbit {
402                                            // Orbit move — screen-space pan using ray/plane intersections
403                                            if let Some(render_view) =
404                                                ui.get_render_view("PolyView")
405                                            {
406                                                let dim = *render_view.dim();
407                                                let viewport_w = dim.x as f32;
408                                                let viewport_h = dim.y as f32;
409                                                if viewport_w > 0.0 && viewport_h > 0.0 {
410                                                    let orbit =
411                                                        &EDITCAMERA.read().unwrap().orbit_camera;
412
413                                                    // Camera basis and parameters
414                                                    let (fwd, right, up) = orbit.basis_vectors();
415                                                    let distance = orbit.distance();
416                                                    let fov = orbit.fov; // vertical fov (radians)
417                                                    let aspect = viewport_w / viewport_h;
418                                                    let tan_half_fov = (fov * 0.5).tan();
419
420                                                    // Orbit target (pivot) is the editing position
421                                                    let target = region.editing_position_3d;
422                                                    // Reconstruct camera position from target, forward and distance
423                                                    let cam_pos = target - fwd * distance;
424
425                                                    // Ground plane at target.y (y-up world)
426                                                    let plane_y = target.y;
427                                                    let eps = 1e-6f32;
428
429                                                    // Helper: build world-space ray dir from pixel offset relative to screen center
430                                                    let ray_dir = |dx_pixels: f32, dy_pixels: f32| -> vek::Vec3<f32> {
431                                                    // Convert to NDC offsets (center = 0,0). Note: screen y grows downward
432                                                    let x_ndc = (dx_pixels) / (viewport_w * 0.5);
433                                                    let y_ndc = (-dy_pixels) / (viewport_h * 0.5);
434                                                    // Camera-space scale along right/up for given pixel offset
435                                                    let sx = x_ndc * tan_half_fov * aspect;
436                                                    let sy = y_ndc * tan_half_fov;
437                                                    (fwd + right * sx + up * sy).normalized()
438                                                };
439
440                                                    let intersect_plane = |orig: vek::Vec3<f32>, dir: vek::Vec3<f32>| -> Option<vek::Vec3<f32>> {
441                                                    if dir.y.abs() < eps { return None; }
442                                                    let t = (plane_y - orig.y) / dir.y;
443                                                    if t.is_finite() { Some(orig + dir * t) } else { None }
444                                                };
445
446                                                    let dir0 = ray_dir(0.0, 0.0);
447                                                    let dir1 =
448                                                        ray_dir(coord.x as f32, coord.y as f32);
449
450                                                    if let (Some(p0), Some(p1)) = (
451                                                        intersect_plane(cam_pos, dir0),
452                                                        intersect_plane(cam_pos, dir1),
453                                                    ) {
454                                                        let delta = p0 - p1;
455                                                        if delta.x.is_finite()
456                                                            && delta.y.is_finite()
457                                                            && delta.z.is_finite()
458                                                        {
459                                                            region.editing_position_3d += delta;
460                                                        }
461                                                    }
462                                                }
463                                            }
464                                        }
465                                        if server_ctx.editor_view_mode == EditorViewMode::Iso {
466                                            // Iso move
467                                            if let Some(render_view) =
468                                                ui.get_render_view("PolyView")
469                                            {
470                                                let dim = *render_view.dim();
471                                                let viewport_h = dim.y as f32;
472
473                                                let (_fwd, right, up) = EDITCAMERA
474                                                    .read()
475                                                    .unwrap()
476                                                    .iso_camera
477                                                    .basis_vectors();
478                                                let ortho_h =
479                                                    EDITCAMERA.read().unwrap().iso_camera.scale();
480                                                let world_per_pixel = (ortho_h) / viewport_h;
481
482                                                let right_xz: Vec3<f32> =
483                                                    vek::Vec3::new(right.x, 0.0, right.z)
484                                                        .normalized();
485                                                let up_xz =
486                                                    vek::Vec3::new(up.x, 0.0, up.z).normalized();
487
488                                                let delta =
489                                                    right_xz * coord.x as f32 * world_per_pixel
490                                                        - up_xz * coord.y as f32 * world_per_pixel;
491
492                                                region.editing_position_3d += delta;
493                                            }
494                                        } else {
495                                            // Firstp move
496                                            let firstp = &EDITCAMERA.read().unwrap().firstp_camera;
497                                            let (forward, right, _up) = firstp.basis_vectors();
498
499                                            let speed = 0.1;
500                                            let dx = coord.x as f32;
501                                            let dy = coord.y as f32;
502                                            let delta =
503                                                forward * (dy) * speed + right * (dx) * speed;
504
505                                            region.editing_position_3d += delta;
506                                        }
507                                        redraw = true;
508                                    }
509                                }
510                            }
511                            if server_ctx.editor_view_mode != EditorViewMode::D2 {
512                                ctx.ui.send(TheEvent::Custom(
513                                    TheId::named("Soft Update Minimap"),
514                                    TheValue::Empty,
515                                ));
516                            }
517                        }
518                    }
519                }
520            }
521            TheEvent::IndexChanged(id, index) => {
522                if id.name == "Map Editor Camera" {
523                    if let Some(region) = project.get_region_mut(&server_ctx.curr_region) {
524                        if *index == 0 {
525                            region.map.camera = MapCamera::TwoD;
526                        } else if *index == 1 {
527                            region.map.camera = MapCamera::ThreeDIso;
528                        } else if *index == 2 {
529                            region.map.camera = MapCamera::ThreeDFirstPerson;
530                        }
531                    }
532                }
533            }
534            TheEvent::ValueChanged(id, value) => {
535                if id.name == "Grid Subdiv" {
536                    if let Some(value) = value.to_i32() {
537                        if let Some(region) = project.get_region_mut(&server_ctx.curr_region) {
538                            region.map.subdivisions = value as f32;
539                        }
540                    }
541                }
542            }
543            TheEvent::StateChanged(id, state) => {
544                if id.name == "linedefAddMidpoint" && *state == TheWidgetState::Clicked {
545                    if let Some(map) = project.get_map_mut(server_ctx) {
546                        let prev = map.clone();
547                        let mut changed = false;
548                        for linedef_id in &map.selected_linedefs.clone() {
549                            map.add_midpoint(*linedef_id);
550                            changed = true;
551                        }
552                        if changed {
553                            self.add_map_undo(map, prev, ctx, server_ctx);
554                        }
555                    }
556                }
557                /*
558                if id.name == "linedefDeleteWall" && *state == TheWidgetState::Clicked {
559                    if let Some(map) = project.get_map_mut(server_ctx) {
560                        let prev = map.clone();
561                        let mut changed = false;
562                        for linedef in &map.selected_linedefs.clone() {
563                            if let Some(linedef) = map.find_linedef_mut(*linedef) {
564                                if !linedef.profile.is_empty() {
565                                    linedef.profile = Map::default();
566                                    changed = true;
567                                }
568                            }
569                        }
570                        if changed {
571                            self.add_map_undo(map, prev, ctx, server_ctx);
572                        }
573                    }
574                    crate::utils::scenemanager_render_map(project, server_ctx);
575                }*/
576                // Region Content List Selection
577                if id.name == "Screen Content List Item" {
578                    if let Some(screen) = project.screens.get_mut(&id.references) {
579                        for sector in screen.map.sectors.iter_mut() {
580                            if sector.creator_id == id.uuid {
581                                screen.map.selected_sectors = vec![sector.id];
582                                RUSTERIX.write().unwrap().set_dirty();
583                                server_ctx.cc = ContentContext::Sector(id.uuid);
584                                // if !sector.properties.contains("source") {
585                                //     // Create default code item
586                                //     if let Some(bytes) = crate::Embedded::get("python/widget.py") {
587                                //         if let Ok(source) = std::str::from_utf8(bytes.data.as_ref())
588                                //         {
589                                //             sector
590                                //                 .properties
591                                //                 .set("source", Value::Str(source.into()));
592                                //         }
593                                //     }
594                                // }
595                                if !sector.properties.contains("data") {
596                                    // Create default data item
597                                    if let Some(bytes) = crate::Embedded::get("toml/widget.toml") {
598                                        if let Ok(source) = std::str::from_utf8(bytes.data.as_ref())
599                                        {
600                                            sector
601                                                .properties
602                                                .set("data", Value::Str(source.into()));
603                                        }
604                                    }
605                                }
606                            }
607                        }
608
609                        let mut center = None;
610                        for sector in screen.map.sectors.iter() {
611                            if sector.creator_id == id.uuid {
612                                center = sector.center(&screen.map.clone());
613                                break;
614                            }
615                        }
616
617                        if let Some(center) = center {
618                            if let Some(render_view) = ui.get_render_view("PolyView") {
619                                let dim = *render_view.dim();
620                                server_ctx.center_map_at_grid_pos(
621                                    Vec2::new(dim.width as f32, dim.height as f32),
622                                    Vec2::new(center.x, center.y),
623                                    &mut screen.map,
624                                );
625                            }
626                        }
627                        set_project_context(
628                            ctx,
629                            ui,
630                            project,
631                            server_ctx,
632                            ProjectContext::ScreenWidget(id.references, id.uuid),
633                        );
634                    }
635                } else
636                // Region Content List Selection
637                if id.name == "Region Content List Item" {
638                    // If this is a character instance, update its name from the template
639
640                    let mut temp_id = None;
641                    let mut temp_name = "".to_string();
642                    if let Some(region) = project.get_region(&server_ctx.curr_region) {
643                        if let Some(character) = region.characters.get(&id.uuid) {
644                            temp_id = Some(character.character_id);
645                        }
646                    }
647
648                    if let Some(temp_id) = temp_id {
649                        if let Some(char_temp) = project.characters.get(&temp_id) {
650                            temp_name = char_temp.name.clone();
651                        }
652                    }
653
654                    if !temp_name.is_empty() {
655                        if let Some(region) = project.get_region_mut(&server_ctx.curr_region) {
656                            if let Some(character) = region.characters.get_mut(&id.uuid) {
657                                character.name = temp_name.clone();
658                            }
659                        }
660                    }
661
662                    // ---
663
664                    let mut character_template_id: Option<Uuid> = None;
665                    let mut found = false;
666                    let mut is_character_instance = false;
667                    if let Some(region) = project.get_region_mut(&server_ctx.curr_region) {
668                        region.map.clear_selection();
669
670                        if let Some(character) = region.characters.get(&id.uuid) {
671                            found = true;
672                            is_character_instance = true;
673
674                            if *SIDEBARMODE.write().unwrap() == SidebarMode::Region {
675                                /*
676                                ui.set_widget_value(
677                                    "CodeEdit",
678                                    ctx,
679                                    TheValue::Text(character.source.clone()),
680                                );*/
681                            } else if *SIDEBARMODE.write().unwrap() == SidebarMode::Character {
682                                character_template_id = Some(character.character_id);
683                            }
684                            region.map.selected_entity_item = Some(id.uuid);
685                            server_ctx.curr_region_content =
686                                ContentContext::CharacterInstance(id.uuid);
687                            server_ctx.cc = ContentContext::CharacterInstance(id.uuid);
688                            if let Some(render_view) = ui.get_render_view("PolyView") {
689                                let dim = *render_view.dim();
690                                let mut position =
691                                    Vec2::new(character.position.x, character.position.z);
692
693                                // If server is running, get the instance position
694                                for entity in region.map.entities.iter() {
695                                    if entity.creator_id == character.id {
696                                        position = entity.get_pos_xz();
697
698                                        break;
699                                    }
700                                }
701
702                                if !server_ctx.content_click_from_map {
703                                    server_ctx.center_map_at_grid_pos(
704                                        Vec2::new(dim.width as f32, dim.height as f32),
705                                        position,
706                                        &mut region.map,
707                                    );
708                                }
709                            }
710                        } else if let Some(item) = region.items.get(&id.uuid) {
711                            found = true;
712                            // if *SIDEBARMODE.write().unwrap() == SidebarMode::Region {
713                            //     ui.set_widget_value(
714                            //         "CodeEdit",
715                            //         ctx,
716                            //         TheValue::Text(item.source.clone()),
717                            //     );
718                            // }
719
720                            region.map.selected_entity_item = Some(id.uuid);
721                            server_ctx.curr_region_content = ContentContext::ItemInstance(id.uuid);
722                            server_ctx.cc = ContentContext::ItemInstance(id.uuid);
723                            if let Some(render_view) = ui.get_render_view("PolyView") {
724                                let dim = *render_view.dim();
725
726                                if !server_ctx.content_click_from_map {
727                                    server_ctx.center_map_at_grid_pos(
728                                        Vec2::new(dim.width as f32, dim.height as f32),
729                                        Vec2::new(item.position.x, item.position.z),
730                                        &mut region.map,
731                                    );
732                                }
733                            }
734                        }
735
736                        if !found {
737                            // Test sectors
738                            for sector in &region.map.sectors.clone() {
739                                if sector.creator_id == id.uuid {
740                                    ui.set_widget_value(
741                                        "CodeEdit",
742                                        ctx,
743                                        TheValue::Text(String::new()),
744                                    );
745
746                                    server_ctx.curr_region_content =
747                                        ContentContext::Sector(id.uuid);
748                                    server_ctx.cc = ContentContext::Sector(id.uuid);
749                                    if let Some(center) = sector.center(&region.map) {
750                                        if let Some(render_view) = ui.get_render_view("PolyView") {
751                                            let dim = *render_view.dim();
752
753                                            server_ctx.center_map_at_grid_pos(
754                                                Vec2::new(dim.width as f32, dim.height as f32),
755                                                center,
756                                                &mut region.map,
757                                            );
758                                        }
759                                    }
760                                }
761                            }
762                        }
763                        server_ctx.content_click_from_map = false;
764                        RUSTERIX.write().unwrap().set_dirty();
765                    }
766
767                    if found {
768                        if is_character_instance {
769                            set_project_context(
770                                ctx,
771                                ui,
772                                project,
773                                server_ctx,
774                                ProjectContext::RegionCharacterInstance(
775                                    server_ctx.curr_region,
776                                    id.uuid,
777                                ),
778                            );
779                        } else {
780                            set_project_context(
781                                ctx,
782                                ui,
783                                project,
784                                server_ctx,
785                                ProjectContext::RegionItemInstance(server_ctx.curr_region, id.uuid),
786                            );
787                        }
788                    }
789
790                    // If in character sidebar mode, set the code aand data
791                    if let Some(character_template_id) = character_template_id {
792                        server_ctx.curr_region_content =
793                            ContentContext::CharacterTemplate(character_template_id);
794                        server_ctx.cc = ContentContext::CharacterTemplate(character_template_id);
795                        // set_code(ui, ctx, project, server_ctx);
796                    }
797                }
798                // Region Selection
799                else if id.name == "Region Item" {
800                    for r in &project.regions {
801                        if r.id == id.uuid {
802                            server_ctx.curr_region = r.id;
803                            redraw = true;
804                        }
805                    }
806                }
807                // An item in the tile list was selected
808                else if id.name == "Tilemap Tile" {
809                    self.curr_tile_uuid = Some(id.uuid);
810                } else if id.name == "Tilemap Editor Add Anim"
811                    || id.name == "Tilemap Editor Add Multi"
812                {
813                    // TILEDRAWER.lock().unwrap().tiles = project.extract_tiles();
814                    // server.update_tiles(project.extract_tiles());
815                }
816            }
817            _ => {}
818        }
819        redraw
820    }
821
822    /// Sets the settings for the map selection.
823    pub fn apply_map_settings(
824        &mut self,
825        ui: &mut TheUI,
826        ctx: &mut TheContext,
827        project: &mut Project,
828        server_ctx: &mut ServerContext,
829    ) {
830        if let Some(id) = server_ctx.pc.id()
831            && server_ctx.pc.is_screen()
832        {
833            let mut sector_id = None;
834            if let Some(map) = project.get_map_mut(server_ctx) {
835                if !map.selected_sectors.is_empty() {
836                    if let Some(sector) = map.find_sector_mut(map.selected_sectors[0]) {
837                        if sector.name.is_empty() {
838                            // sector.name = "Unnamed".to_string();
839                            ctx.ui.send(TheEvent::StateChanged(
840                                TheId::named_with_id_and_reference(
841                                    "Screen Content List Item",
842                                    sector.creator_id,
843                                    sector.creator_id,
844                                ),
845                                TheWidgetState::Clicked,
846                            ));
847                        }
848
849                        sector_id = Some(sector.creator_id);
850                    }
851                }
852            }
853
854            if let Some(tree_layout) = ui.get_tree_layout("Project Tree") {
855                if let Some(node) = tree_layout.get_node_by_id_mut(&id) {
856                    if let Some(screen) = project.screens.get(&id) {
857                        gen_screen_tree_items(node, screen);
858                    }
859                    if let Some(sector_id) = sector_id {
860                        node.new_item_selected(&TheId::named_with_id_and_reference(
861                            "Screen Content List Item",
862                            sector_id,
863                            id,
864                        ));
865                    }
866                }
867            }
868        }
869
870        /*
871
872        // Create Node Settings if necessary
873        if let Some(layout) = ui.get_text_layout("Node Settings") {
874            layout.clear();
875        }
876
877        if server_ctx.curr_map_tool_type != MapToolType::Sector {
878            CODEEDITOR.write().unwrap().clear_shader(ui, ctx);
879        }
880
881        if let Some(map) = project.get_map_mut(server_ctx) {
882            if server_ctx.curr_map_tool_type == MapToolType::Linedef
883                && !map.selected_linedefs.is_empty()
884            {
885                self.create_linedef_settings(map, map.selected_linedefs[0], ui, ctx, server_ctx);
886            } else if (server_ctx.curr_map_tool_type == MapToolType::Sector
887                || server_ctx.curr_map_tool_type == MapToolType::Rect)
888                && !map.selected_sectors.is_empty()
889            {
890                if server_ctx.get_map_context() == MapContext::Screen {
891                    // In Screen Context make sure we create the default code and data items
892                    if let Some(layout) = ui.get_list_layout("Screen Content List") {
893                        if let Some(sector) = map.find_sector_mut(map.selected_sectors[0]) {
894                            if sector.name.is_empty() {
895                                sector.name = "Unnamed".to_string();
896                                ctx.ui.send(TheEvent::Custom(
897                                    TheId::named("Update Content List"),
898                                    TheValue::Empty,
899                                ));
900                                ctx.ui.send(TheEvent::StateChanged(
901                                    TheId::named_with_id_and_reference(
902                                        "Screen Content List Item",
903                                        sector.creator_id,
904                                        sector.creator_id,
905                                    ),
906                                    TheWidgetState::Clicked,
907                                ));
908                            }
909                            layout.select_item(sector.creator_id, ctx, true);
910                        }
911                    }
912                }
913                self.create_sector_settings(map, map.selected_sectors[0], ui, ctx, server_ctx);
914            } else if server_ctx.curr_map_tool_type == MapToolType::Vertex
915                && !map.selected_vertices.is_empty()
916            {
917                self.create_vertex_settings(map, map.selected_vertices[0], ui, ctx, server_ctx);
918            }
919        }*/
920    }
921
922    /// Adds an undo step for the given map change.
923    fn add_map_undo(
924        &self,
925        _map: &Map,
926        _prev: Map,
927        _ctx: &mut TheContext,
928        _server_ctx: &ServerContext,
929    ) {
930        RUSTERIX.write().unwrap().set_dirty();
931    }
932}