1use crate::editor::RUSTERIX;
2use crate::hud::{Hud, HudMode};
3use crate::prelude::*;
4use MapEvent::*;
5use ToolEvent::*;
6use rusterix::prelude::*;
7use vek::Vec2;
8
9pub struct RectTool {
10 id: TheId,
11
12 hovered_vertices: Option<[Vec2<f32>; 4]>,
13 mode: i32,
14 hud: Hud,
15
16 processed: FxHashSet<Vec2<i32>>,
17
18 stroke_active: bool,
19 stroke_changed: bool,
20 stroke_prev_map: Option<Map>,
21 stroke_work_map: Option<Map>,
22 last_2d_cell: Option<Vec2<i32>>,
23 line_start_2d_cell: Option<Vec2<i32>>,
24 line_axis_horizontal: Option<bool>,
25}
26
27impl Tool for RectTool {
28 fn new() -> Self
29 where
30 Self: Sized,
31 {
32 Self {
33 id: TheId::named("Rect Tool"),
34
35 hovered_vertices: None,
36 mode: 0,
37 hud: Hud::new(HudMode::Rect),
38
39 processed: FxHashSet::default(),
40
41 stroke_active: false,
42 stroke_changed: false,
43 stroke_prev_map: None,
44 stroke_work_map: None,
45 last_2d_cell: None,
46 line_start_2d_cell: None,
47 line_axis_horizontal: None,
48 }
49 }
50
51 fn id(&self) -> TheId {
52 self.id.clone()
53 }
54 fn info(&self) -> String {
55 fl!("tool_rect")
56 }
57 fn icon_name(&self) -> String {
58 str!("square")
59 }
60 fn accel(&self) -> Option<char> {
61 Some('R')
62 }
63
64 fn help_url(&self) -> Option<String> {
65 Some("docs/creator/tools/rect".to_string())
66 }
67
68 fn tool_event(
69 &mut self,
70 tool_event: ToolEvent,
71 _ui: &mut TheUI,
72 _ctx: &mut TheContext,
73 project: &mut Project,
74 server_ctx: &mut ServerContext,
75 ) -> bool {
76 match tool_event {
77 Activate => {
78 server_ctx.curr_map_tool_type = MapToolType::Rect;
79
80 if let Some(region) = project.get_region_mut(&server_ctx.curr_region) {
81 region.map.selected_vertices.clear();
82 region.map.selected_linedefs.clear();
83 region.map.selected_sectors.clear();
84 }
85
86 return true;
89 }
90 DeActivate => {
91 server_ctx.curr_map_tool_type = MapToolType::General;
92 server_ctx.hover_cursor = None;
93 self.reset_stroke();
94 if let Some(region) = project.get_region_mut(&server_ctx.curr_region) {
95 region.map.clear_temp();
96 }
97 return true;
98 }
99 _ => {}
100 };
101
102 false
103 }
104
105 fn map_event(
106 &mut self,
107 map_event: MapEvent,
108 ui: &mut TheUI,
109 ctx: &mut TheContext,
110 map: &mut Map,
111 server_ctx: &mut ServerContext,
112 ) -> Option<ProjectUndoAtom> {
113 let mut undo_atom: Option<ProjectUndoAtom> = None;
114
115 fn add_tile(
117 ui: &mut TheUI,
118 _ctx: &mut TheContext,
119 map: &mut Map,
120 server_ctx: &mut ServerContext,
121 hovered_vertices: Option<[Vec2<f32>; 4]>,
122 mode: i32,
123 ) -> Option<ProjectUndoAtom> {
124 let mut undo_atom: Option<ProjectUndoAtom> = None;
125 if let Some(vertices) = hovered_vertices {
128 let mut add_it = true;
129 let mut layer: u8 = 0;
130
131 if ui.shift {
132 if let Some(ev0) = map.find_vertex_at(vertices[0].x, vertices[0].y) {
134 if let Some(ev1) = map.find_vertex_at(vertices[1].x, vertices[1].y) {
135 if let Some(ev2) = map.find_vertex_at(vertices[2].x, vertices[2].y) {
136 if let Some(ev3) = map.find_vertex_at(vertices[3].x, vertices[3].y)
137 {
138 let sectors =
139 map.find_sectors_with_vertex_indices(&[ev0, ev1, ev2, ev3]);
140
141 if let Some(sector_id) = sectors.last() {
142 let prev = map.clone();
143 let mut lines = vec![];
144 if let Some(s) = map.find_sector(*sector_id) {
145 lines = s.linedefs.clone();
146 }
147 map.delete_elements(&[], &lines, &[*sector_id]);
148 undo_atom = Some(ProjectUndoAtom::MapEdit(
149 server_ctx.pc,
150 Box::new(prev),
151 Box::new(map.clone()),
152 ));
153 }
154 }
155 }
156 }
157 }
158 } else if let Some(source) = get_source(ui, server_ctx) {
159 if let Some(ev0) = map.find_vertex_at(vertices[0].x, vertices[0].y) {
162 if let Some(ev1) = map.find_vertex_at(vertices[1].x, vertices[1].y) {
163 if let Some(ev2) = map.find_vertex_at(vertices[2].x, vertices[2].y) {
164 if let Some(ev3) = map.find_vertex_at(vertices[3].x, vertices[3].y)
165 {
166 let sectors =
167 map.find_sectors_with_vertex_indices(&[ev0, ev1, ev2, ev3]);
168
169 let prev = map.clone();
170 for sector_id in sectors {
171 if let Some(sector) = map.find_sector_mut(sector_id) {
172 if let Some(sector_floor_source) =
173 sector.properties.get_default_source()
174 {
175 if let Some(l) = §or.layer {
177 if *l > layer {
178 layer = *l;
179 }
180 }
181
182 if source == *sector_floor_source {
183 add_it = false;
185 } else if mode == 0 {
186 sector.properties.set(
188 "source",
189 Value::Source(source.clone()),
190 );
191
192 undo_atom = Some(ProjectUndoAtom::MapEdit(
193 server_ctx.pc,
194 Box::new(prev),
195 Box::new(map.clone()),
196 ));
197
198 add_it = false;
199 break;
200 }
201 }
202 }
203 }
204 }
205 }
206 }
207 }
208
209 if add_it {
210 let v0 = map.add_vertex_at(vertices[0].x, vertices[0].y);
211 let v1 = map.add_vertex_at(vertices[1].x, vertices[1].y);
212 let v2 = map.add_vertex_at(vertices[2].x, vertices[2].y);
213 let v3 = map.add_vertex_at(vertices[3].x, vertices[3].y);
214
215 map.possible_polygon = vec![];
216 let _ = map.create_linedef_manual(v0, v1);
217 let _ = map.create_linedef_manual(v1, v2);
218 let _ = map.create_linedef_manual(v2, v3);
219 let _ = map.create_linedef_manual(v3, v0);
220 let sid = map.close_polygon_manual();
221
222 if let Some(sector_id) = sid {
223 let prev = map.clone();
242 if let Some(sector) = map.find_sector_mut(sector_id) {
243 sector.properties.set("rect", Value::Bool(true));
244
245 sector.properties.set("source", Value::Source(source));
246 sector.layer = Some(layer + 1);
247 }
248
249 undo_atom = Some(ProjectUndoAtom::MapEdit(
250 server_ctx.pc,
251 Box::new(prev),
252 Box::new(map.clone()),
253 ));
254
255 map.selected_vertices.clear();
256 map.selected_linedefs.clear();
257 map.selected_sectors = vec![sector_id];
258 }
259 }
260 }
261 }
262
263 undo_atom
264 }
265
266 fn apply_hover(
267 coord: Vec2<i32>,
268 ui: &mut TheUI,
269 _ctx: &mut TheContext,
270 map: &mut Map,
271 server_ctx: &mut ServerContext,
272 ) -> Option<[Vec2<f32>; 4]> {
273 let mut hovered_vertices: Option<[Vec2<f32>; 4]> = None;
274
275 if let Some(render_view) = ui.get_render_view("PolyView") {
276 let dim = *render_view.dim();
277 server_ctx.hover = (None, None, None);
278 let cp = server_ctx.local_to_map_cell(
279 Vec2::new(dim.width as f32, dim.height as f32),
280 Vec2::new(coord.x as f32, coord.y as f32),
281 map,
282 1.0,
283 );
284 let step = 1.0;
285 map.curr_rectangle = Some((cp, cp + step));
286 hovered_vertices = Some([
287 cp,
288 cp + Vec2::new(0.0, step),
289 cp + Vec2::new(step, step),
290 cp + Vec2::new(step, 0.0),
291 ]);
292 server_ctx.hover_cursor = Some(cp);
293 if map.properties.get_bool_default("terrain_enabled", false) {
294 server_ctx.rect_terrain_id = Some((cp.x.floor() as i32, cp.y.floor() as i32));
295 } else {
296 server_ctx.rect_terrain_id = None;
297 }
298 }
299
300 hovered_vertices
301 }
302
303 match map_event {
304 MapKey(_c) => {}
305 MapClicked(coord) => {
306 if self.hud.clicked(coord.x, coord.y, map, ui, ctx, server_ctx) {
307 crate::editor::RUSTERIX.write().unwrap().set_dirty();
308 return None;
309 }
310
311 self.processed.clear();
312 if server_ctx.editor_view_mode == EditorViewMode::D2 {
313 let use_terrain_paint =
314 map.properties.get_bool_default("terrain_enabled", false);
315 if let Some(cp) = server_ctx.hover_cursor {
316 self.begin_stroke_if_needed(map);
317 let k = if use_terrain_paint {
318 Vec2::new(cp.x.floor() as i32, cp.y.floor() as i32)
319 } else {
320 Vec2::new(cp.x.floor() as i32, cp.y.floor() as i32)
321 };
322 if ui.ctrl && !use_terrain_paint {
323 self.line_start_2d_cell = Some(k);
324 self.line_axis_horizontal = None;
325 }
326 if let Some(work_map) = self.stroke_work_map.as_mut() {
327 let changed = if use_terrain_paint {
328 server_ctx.rect_terrain_id = Some((k.x, k.y));
329 Self::apply_3d_paint_at_current_target(work_map, ui, server_ctx)
330 .is_some()
331 } else {
332 let step = 1.0;
333 let x = k.x as f32 * step;
334 let y = k.y as f32 * step;
335 let verts = Some([
336 Vec2::new(x, y),
337 Vec2::new(x, y + step),
338 Vec2::new(x + step, y + step),
339 Vec2::new(x + step, y),
340 ]);
341 add_tile(ui, ctx, work_map, server_ctx, verts, self.mode).is_some()
342 };
343 if changed {
344 self.stroke_changed = true;
345 }
346 self.processed.insert(k);
347 self.last_2d_cell = Some(k);
348 }
349 }
350 } else {
351 self.compute_3d_tile(coord, map, ui, server_ctx);
352 self.begin_stroke_if_needed(map);
353 if let Some(work_map) = self.stroke_work_map.as_mut()
354 && let Some(key) =
355 Self::apply_3d_paint_at_current_target(work_map, ui, server_ctx)
356 && !self.processed.contains(&key)
357 {
358 self.stroke_changed = true;
359 self.processed.insert(key);
360 }
361 }
362 }
363 MapDragged(coord) => {
364 if self.hud.dragged(coord.x, coord.y, map, ui, ctx, server_ctx) {
365 crate::editor::RUSTERIX.write().unwrap().set_dirty();
366 return None;
367 }
368 if server_ctx.editor_view_mode == EditorViewMode::D2 {
369 let use_terrain_paint =
370 map.properties.get_bool_default("terrain_enabled", false);
371 self.hovered_vertices = apply_hover(coord, ui, ctx, map, server_ctx);
372 if let Some(cp) = server_ctx.hover_cursor {
373 self.begin_stroke_if_needed(map);
374 let k = if use_terrain_paint {
375 Vec2::new(cp.x.floor() as i32, cp.y.floor() as i32)
376 } else {
377 Vec2::new(cp.x.floor() as i32, cp.y.floor() as i32)
378 };
379 if let Some(work_map) = self.stroke_work_map.as_mut() {
380 let line_anchor = self.line_start_2d_cell.unwrap_or(k);
381 let mut to = k;
382 if ui.ctrl && !use_terrain_paint {
383 if self.line_axis_horizontal.is_none() {
384 let dx = (to.x - line_anchor.x).abs();
385 let dy = (to.y - line_anchor.y).abs();
386 if dx > 0 || dy > 0 {
387 self.line_axis_horizontal = Some(dx >= dy);
388 }
389 }
390 if let Some(horizontal) = self.line_axis_horizontal {
391 if horizontal {
392 to.y = line_anchor.y;
393 } else {
394 to.x = line_anchor.x;
395 }
396 }
397 }
398
399 let from = self.last_2d_cell.unwrap_or(to);
400 let step = 1.0;
401 let between = Self::cells_between(from, to);
402 for cell in between.iter().copied() {
403 if self.processed.contains(&cell) {
404 continue;
405 }
406 let changed = if use_terrain_paint {
407 server_ctx.rect_terrain_id = Some((cell.x, cell.y));
408 Self::apply_3d_paint_at_current_target(work_map, ui, server_ctx)
409 .is_some()
410 } else {
411 let x = cell.x as f32 * step;
412 let y = cell.y as f32 * step;
413 let verts = Some([
414 Vec2::new(x, y),
415 Vec2::new(x, y + step),
416 Vec2::new(x + step, y + step),
417 Vec2::new(x + step, y),
418 ]);
419 add_tile(ui, ctx, work_map, server_ctx, verts, self.mode)
420 .is_some()
421 };
422 if changed {
423 self.stroke_changed = true;
424 }
425 self.processed.insert(cell);
426 }
427 self.last_2d_cell = Some(to);
428 }
429 }
430 } else {
431 self.compute_3d_tile(coord, map, ui, server_ctx);
432 self.begin_stroke_if_needed(map);
433 if let Some(work_map) = self.stroke_work_map.as_mut()
434 && let Some(key) =
435 Self::apply_3d_paint_at_current_target(work_map, ui, server_ctx)
436 && !self.processed.contains(&key)
437 {
438 self.stroke_changed = true;
439 self.processed.insert(key);
440 }
441 }
442 }
443 MapUp(_) => {
444 if self.stroke_active {
445 if self.stroke_changed
446 && let (Some(prev), Some(new_map)) =
447 (self.stroke_prev_map.take(), self.stroke_work_map.take())
448 {
449 *map = new_map;
450 undo_atom = Some(ProjectUndoAtom::MapEdit(
451 server_ctx.pc,
452 Box::new(prev),
453 Box::new(map.clone()),
454 ));
455 }
456 self.reset_stroke();
457 }
458 }
459 MapHover(coord) => {
460 if server_ctx.editor_view_mode == EditorViewMode::D2 {
461 self.hovered_vertices = apply_hover(coord, ui, ctx, map, server_ctx);
462 } else {
463 self.compute_3d_tile(coord, map, ui, server_ctx);
464 }
465 }
466 MapDelete => {}
467 MapEscape => {
468 self.reset_stroke();
469 map.clear_temp();
470 crate::editor::RUSTERIX.write().unwrap().set_dirty();
471 }
472 }
473 undo_atom
474 }
475
476 fn draw_hud(
477 &mut self,
478 buffer: &mut TheRGBABuffer,
479 map: &mut Map,
480 ctx: &mut TheContext,
481 server_ctx: &mut ServerContext,
482 assets: &Assets,
483 ) {
484 let id = if !map.selected_linedefs.is_empty() {
485 Some(map.selected_linedefs[0])
486 } else {
487 None
488 };
489 self.hud.draw(buffer, map, ctx, server_ctx, id, assets);
490 }
491
492 fn handle_event(
493 &mut self,
494 event: &TheEvent,
495 _ui: &mut TheUI,
496 _ctx: &mut TheContext,
497 project: &mut Project,
498 server_ctx: &mut ServerContext,
499 ) -> bool {
500 let redraw = false;
501 #[allow(clippy::single_match)]
502 match event {
503 TheEvent::StateChanged(id, state) => {
504 #[allow(clippy::collapsible_if)]
505 if id.name == "Apply Map Properties" && *state == TheWidgetState::Clicked {
506 let mut source: Option<Value> = None;
507
508 if let Some(id) = server_ctx.curr_tile_id {
509 source = Some(Value::Source(PixelSource::TileId(id)));
510 }
511
512 if let Some(source) = source {
513 if let Some(map) = project.get_map_mut(server_ctx) {
514 let _prev = map.clone();
515
516 for linedef_id in map.selected_linedefs.clone() {
517 if let Some(linedef) = map.find_linedef_mut(linedef_id) {
518 if self.hud.selected_icon_index == 0 {
519 linedef.properties.set("row1_source", source.clone());
520 } else if self.hud.selected_icon_index == 1 {
521 linedef.properties.set("row2_source", source.clone());
522 } else if self.hud.selected_icon_index == 2 {
523 linedef.properties.set("row3_source", source.clone());
524 } else if self.hud.selected_icon_index == 3 {
525 linedef.properties.set("row4_source", source.clone());
526 }
527 crate::editor::RUSTERIX.write().unwrap().set_dirty();
528 }
529 }
530
531 crate::editor::RUSTERIX.write().unwrap().set_dirty();
532 }
533 }
534 } else if id.name == "Remove Map Properties" && *state == TheWidgetState::Clicked {
535 if let Some(map) = project.get_map_mut(server_ctx) {
536 let _prev = map.clone();
537
538 for linedef_id in map.selected_linedefs.clone() {
539 if let Some(linedef) = map.find_linedef_mut(linedef_id) {
540 if self.hud.selected_icon_index == 0 {
541 linedef
542 .properties
543 .set("row1_source", Value::Source(PixelSource::Off));
544 } else if self.hud.selected_icon_index == 1 {
545 linedef
546 .properties
547 .set("row2_source", Value::Source(PixelSource::Off));
548 } else if self.hud.selected_icon_index == 2 {
549 linedef
550 .properties
551 .set("row3_source", Value::Source(PixelSource::Off));
552 } else if self.hud.selected_icon_index == 3 {
553 linedef
554 .properties
555 .set("row4_source", Value::Source(PixelSource::Off));
556 }
557 crate::editor::RUSTERIX.write().unwrap().set_dirty();
558 }
559 }
560
561 crate::editor::RUSTERIX.write().unwrap().set_dirty();
562 }
563 }
564 }
565 _ => {}
566 }
567 redraw
568 }
569}
570
571impl RectTool {
572 fn begin_stroke_if_needed(&mut self, map: &Map) {
573 if !self.stroke_active {
574 self.stroke_active = true;
575 self.stroke_changed = false;
576 self.stroke_prev_map = Some(map.clone());
577 self.stroke_work_map = Some(map.clone());
578 self.processed.clear();
579 }
580 }
581
582 fn reset_stroke(&mut self) {
583 self.stroke_active = false;
584 self.stroke_changed = false;
585 self.stroke_prev_map = None;
586 self.stroke_work_map = None;
587 self.last_2d_cell = None;
588 self.line_start_2d_cell = None;
589 self.line_axis_horizontal = None;
590 self.processed.clear();
591 }
592
593 fn cells_between(a: Vec2<i32>, b: Vec2<i32>) -> Vec<Vec2<i32>> {
594 let mut out = Vec::new();
595 let mut x0 = a.x;
596 let mut y0 = a.y;
597 let x1 = b.x;
598 let y1 = b.y;
599
600 let dx = (x1 - x0).abs();
601 let sx = if x0 < x1 { 1 } else { -1 };
602 let dy = -(y1 - y0).abs();
603 let sy = if y0 < y1 { 1 } else { -1 };
604 let mut err = dx + dy;
605
606 loop {
607 out.push(Vec2::new(x0, y0));
608 if x0 == x1 && y0 == y1 {
609 break;
610 }
611 let e2 = 2 * err;
612 if e2 >= dy {
613 err += dy;
614 x0 += sx;
615 }
616 if e2 <= dx {
617 err += dx;
618 y0 += sy;
619 }
620 }
621
622 out
623 }
624
625 fn apply_3d_paint_at_current_target(
626 map: &mut Map,
627 ui: &TheUI,
628 server_ctx: &ServerContext,
629 ) -> Option<Vec2<i32>> {
630 let curr_tile_id = server_ctx.curr_tile_id;
631
632 if let Some((x, z)) = server_ctx.rect_terrain_id {
633 let mut tiles = match map.properties.get("tiles") {
634 Some(Value::TileOverrides(existing)) => existing.clone(),
635 _ => FxHashMap::default(),
636 };
637
638 let mut blend_tiles = match map.properties.get("blend_tiles") {
639 Some(Value::BlendOverrides(existing)) => existing.clone(),
640 _ => FxHashMap::default(),
641 };
642
643 if ui.shift {
644 tiles.remove(&(x, z));
645 blend_tiles.remove(&(x, z));
646 } else if let Some(tile_id) = curr_tile_id {
647 if server_ctx.rect_blend_preset == VertexBlendPreset::Solid {
648 tiles.insert((x, z), PixelSource::TileId(tile_id));
649 blend_tiles.remove(&(x, z));
650 } else {
651 blend_tiles.insert(
652 (x, z),
653 (server_ctx.rect_blend_preset, PixelSource::TileId(tile_id)),
654 );
655 tiles.remove(&(x, z));
656 }
657 } else {
658 return None;
659 }
660
661 if tiles.is_empty() {
662 map.properties.remove("tiles");
663 } else {
664 map.properties.set("tiles", Value::TileOverrides(tiles));
665 }
666 if blend_tiles.is_empty() {
667 map.properties.remove("blend_tiles");
668 } else {
669 map.properties
670 .set("blend_tiles", Value::BlendOverrides(blend_tiles));
671 }
672
673 return Some(Vec2::new(x, z));
674 }
675
676 if let Some(sector_id) = server_ctx.rect_sector_id_3d
677 && let Some(sector) = map.find_sector_mut(sector_id)
678 {
679 let key = Vec2::new(server_ctx.rect_tile_id_3d.0, server_ctx.rect_tile_id_3d.1);
680
681 let mut tiles = match sector.properties.get("tiles") {
682 Some(Value::TileOverrides(existing)) => existing.clone(),
683 _ => FxHashMap::default(),
684 };
685 let mut blend_tiles = match sector.properties.get("blend_tiles") {
686 Some(Value::BlendOverrides(existing)) => existing.clone(),
687 _ => FxHashMap::default(),
688 };
689
690 if ui.shift {
691 tiles.remove(&server_ctx.rect_tile_id_3d);
692 blend_tiles.remove(&server_ctx.rect_tile_id_3d);
693 } else if let Some(tile_id) = curr_tile_id {
694 if server_ctx.rect_blend_preset == VertexBlendPreset::Solid {
695 tiles.insert(server_ctx.rect_tile_id_3d, PixelSource::TileId(tile_id));
696 blend_tiles.remove(&server_ctx.rect_tile_id_3d);
697 } else {
698 blend_tiles.insert(
699 server_ctx.rect_tile_id_3d,
700 (server_ctx.rect_blend_preset, PixelSource::TileId(tile_id)),
701 );
702 tiles.remove(&server_ctx.rect_tile_id_3d);
703 }
704 } else {
705 return None;
706 }
707
708 if tiles.is_empty() {
709 sector.properties.remove("tiles");
710 } else {
711 sector.properties.set("tiles", Value::TileOverrides(tiles));
712 }
713 if blend_tiles.is_empty() {
714 sector.properties.remove("blend_tiles");
715 } else {
716 sector
717 .properties
718 .set("blend_tiles", Value::BlendOverrides(blend_tiles));
719 }
720
721 return Some(key);
722 }
723
724 None
725 }
726
727 fn compute_3d_tile(
728 &mut self,
729 coord: Vec2<i32>,
730 map: &Map,
731 ui: &mut TheUI,
732 server_ctx: &mut ServerContext,
733 ) {
734 if let Some(render_view) = ui.get_render_view("PolyView") {
735 let dim = *render_view.dim();
736
737 let screen_uv = [
738 coord.x as f32 / dim.width as f32,
739 coord.y as f32 / dim.height as f32,
740 ];
741
742 let rusterix = RUSTERIX.read().unwrap();
743
744 let rc = rusterix.scene_handler.vm.pick_geo_id_at_uv(
745 dim.width as u32,
746 dim.height as u32,
747 screen_uv,
748 false,
749 false,
750 );
751
752 let mut found = false;
753 if let Some((scenevm::GeoId::Sector(id), world_hit, _)) = rc {
754 let mut best: Option<((i32, i32), f32)> = None;
755
756 for surface in map.surfaces.values() {
757 if surface.sector_id != id {
758 continue;
759 }
760
761 let n = surface.plane.normal;
762 let n_len = n.magnitude();
763 if n_len <= 1e-6 {
764 continue;
765 }
766
767 let signed_dist = (world_hit - surface.plane.origin).dot(n / n_len);
770 let dist = signed_dist.abs();
771 let tile = surface.world_to_tile_local(world_hit, map);
772
773 if best
774 .as_ref()
775 .map(|(_, best_dist)| dist < *best_dist)
776 .unwrap_or(true)
777 {
778 best = Some((tile, dist));
779 }
780 }
781
782 if let Some((tile, _)) = best {
783 server_ctx.rect_tile_id_3d = tile;
784 server_ctx.rect_sector_id_3d = Some(id);
785 found = true;
786 }
787 }
788
789 if let Some((scenevm::GeoId::Terrain(x, z), _, _)) = rc {
790 server_ctx.rect_terrain_id = Some((x, z));
791 } else {
792 server_ctx.rect_terrain_id = None;
793 }
794
795 if !found {
796 server_ctx.rect_sector_id_3d = None;
797 }
798 }
799 }
800}