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 ®ion.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(®ion.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}