1use crate::hud::{Hud, HudMode};
2use crate::prelude::*;
3use MapEvent::*;
4use ToolEvent::*;
5use rusterix::Assets;
6
7pub struct SelectionTool {
8 id: TheId,
9 click_pos: Vec2<f32>,
10 rectangle_undo_map: Map,
11 hud: Hud,
12}
13
14impl Tool for SelectionTool {
15 fn new() -> Self
16 where
17 Self: Sized,
18 {
19 Self {
20 id: TheId::named("Select Tool"),
21 click_pos: Vec2::zero(),
22 rectangle_undo_map: Map::default(),
23 hud: Hud::new(HudMode::Selection),
24 }
25 }
26
27 fn id(&self) -> TheId {
28 self.id.clone()
29 }
30 fn info(&self) -> String {
31 if cfg!(target_os = "macos") {
32 fl!("tool_selection_mac")
33 } else {
34 fl!("tool_selection")
35 }
36 }
37 fn icon_name(&self) -> String {
38 str!("cursor")
39 }
40 fn accel(&self) -> Option<char> {
41 Some('S')
42 }
43
44 fn help_url(&self) -> Option<String> {
45 Some("docs/creator/tools/selection".to_string())
46 }
47
48 fn tool_event(
49 &mut self,
50 tool_event: ToolEvent,
51 ui: &mut TheUI,
52 _ctx: &mut TheContext,
53 _project: &mut Project,
54 server_ctx: &mut ServerContext,
55 ) -> bool {
56 match tool_event {
57 Activate => {
58 server_ctx.curr_map_tool_type = MapToolType::Selection;
59
60 return true;
61 }
62 DeActivate => {
63 if let Some(layout) = ui.get_hlayout("Game Tool Params") {
64 layout.clear();
65 layout.set_reverse_index(None);
66 }
67 server_ctx.curr_map_tool_type = MapToolType::General;
68 return true;
69 }
70 _ => {}
71 };
72 false
73 }
74
75 fn map_event(
76 &mut self,
77 map_event: MapEvent,
78 ui: &mut TheUI,
79 ctx: &mut TheContext,
80 map: &mut Map,
81 server_ctx: &mut ServerContext,
82 ) -> Option<ProjectUndoAtom> {
83 let mut undo_atom: Option<ProjectUndoAtom> = None;
84
85 match map_event {
86 MapKey(c) => {
87 match c {
88 '1'..='9' => map.subdivisions = (c as u8 - b'0') as f32,
89 '0' => map.subdivisions = 10.0,
90 _ => {}
91 }
92 crate::editor::RUSTERIX.write().unwrap().set_dirty();
93 }
94 MapClicked(coord) => {
95 if self.hud.clicked(coord.x, coord.y, map, ui, ctx, server_ctx) {
96 crate::editor::RUSTERIX.write().unwrap().set_dirty();
97 return None;
98 }
99
100 if !server_ctx.hover_is_empty() {
101 let prev = map.clone();
102 let arrays = server_ctx.hover_to_arrays();
103 if ui.shift {
104 map.add_to_selection(arrays.0, arrays.1, arrays.2);
106 } else if ui.alt {
107 map.remove_from_selection(arrays.0, arrays.1, arrays.2);
109 } else {
110 map.selected_vertices = arrays.0;
112 map.selected_linedefs = arrays.1;
113 map.selected_sectors = arrays.2;
114 }
115
116 undo_atom = Some(ProjectUndoAtom::MapEdit(
117 server_ctx.pc,
118 Box::new(prev),
119 Box::new(map.clone()),
120 ));
121
122 ctx.ui.send(TheEvent::Custom(
123 TheId::named("Map Selection Changed"),
124 TheValue::Empty,
125 ));
126 }
127
128 self.click_pos = Vec2::new(coord.x as f32, coord.y as f32);
129 self.rectangle_undo_map = map.clone();
130 }
131 MapDragged(coord) => {
132 if server_ctx.editor_view_mode != EditorViewMode::D2 {
133 if let Some(render_view) = ui.get_render_view("PolyView") {
134 let dim = *render_view.dim();
135 let click_pos = server_ctx.local_to_map_grid(
136 Vec2::new(dim.width as f32, dim.height as f32),
137 self.click_pos,
138 map,
139 map.subdivisions,
140 );
141 let drag_pos = server_ctx.local_to_map_grid(
142 Vec2::new(dim.width as f32, dim.height as f32),
143 Vec2::new(coord.x as f32, coord.y as f32),
144 map,
145 map.subdivisions,
146 );
147
148 let top_left =
149 Vec2::new(click_pos.x.min(drag_pos.x), click_pos.y.min(drag_pos.y));
150 let bottom_right =
151 Vec2::new(click_pos.x.max(drag_pos.x), click_pos.y.max(drag_pos.y));
152
153 let selection =
154 server_ctx.geometry_in_rectangle(top_left, bottom_right, map);
155
156 *map = self.rectangle_undo_map.clone();
157 map.curr_rectangle = Some((click_pos, drag_pos));
158
159 if ui.shift {
160 map.add_to_selection(selection.0, selection.1, selection.2);
162 } else if ui.alt {
163 map.remove_from_selection(selection.0, selection.1, selection.2);
165 } else {
166 map.selected_vertices = selection.0;
168 map.selected_linedefs = selection.1;
169 map.selected_sectors = selection.2;
170 }
171 }
172 crate::editor::RUSTERIX.write().unwrap().set_dirty();
173 }
174 }
175 MapUp(_) => {
176 if server_ctx.editor_view_mode != EditorViewMode::D2 {
177 if map.curr_rectangle.is_some() {
178 map.curr_rectangle = None;
179
180 undo_atom = Some(ProjectUndoAtom::MapEdit(
181 server_ctx.pc,
182 Box::new(self.rectangle_undo_map.clone()),
183 Box::new(map.clone()),
184 ));
185
186 ctx.ui.send(TheEvent::Custom(
187 TheId::named("Map Selection Changed"),
188 TheValue::Empty,
189 ));
190 }
191 }
192 }
193 MapHover(coord) => {
194 if self.hud.hovered(coord.x, coord.y, map, ui, ctx, server_ctx) {
195 crate::editor::RUSTERIX.write().unwrap().set_dirty();
196 return None;
197 }
198
199 if let Some(render_view) = ui.get_render_view("PolyView") {
200 let dim = *render_view.dim();
201 server_ctx.hover = server_ctx.geometry_at(
202 Vec2::new(dim.width as f32, dim.height as f32),
203 Vec2::new(coord.x as f32, coord.y as f32),
204 map,
205 );
206
207 let cp = server_ctx.local_to_map_grid(
208 Vec2::new(dim.width as f32, dim.height as f32),
209 Vec2::new(coord.x as f32, coord.y as f32),
210 map,
211 map.subdivisions,
212 );
213 ctx.ui.send(TheEvent::Custom(
214 TheId::named("Cursor Pos Changed"),
215 TheValue::Float2(cp),
216 ));
217 server_ctx.hover_cursor = Some(cp);
218 }
219 }
220 MapDelete => {
221 if !map.selected_vertices.is_empty()
222 || !map.selected_linedefs.is_empty()
223 || !map.selected_sectors.is_empty()
224 {
225 let prev = map.clone();
226
227 let vertices = map.selected_vertices.clone();
228 let linedefs = map.selected_linedefs.clone();
229 let sectors = map.selected_sectors.clone();
230
231 map.delete_elements(&vertices, &linedefs, §ors);
232 map.selected_vertices.clear();
233 map.selected_linedefs.clear();
234 map.selected_sectors.clear();
235
236 undo_atom = Some(ProjectUndoAtom::MapEdit(
237 server_ctx.pc,
238 Box::new(prev),
239 Box::new(map.clone()),
240 ));
241
242 ctx.ui.send(TheEvent::Custom(
243 TheId::named("Map Selection Changed"),
244 TheValue::Empty,
245 ));
246 }
247 }
248 MapEscape => {
249 if !map.selected_vertices.is_empty()
251 || !map.selected_linedefs.is_empty()
252 || !map.selected_sectors.is_empty()
253 {
254 let prev = map.clone();
255
256 map.selected_vertices.clear();
257 map.selected_linedefs.clear();
258 map.selected_sectors.clear();
259
260 undo_atom = Some(ProjectUndoAtom::MapEdit(
261 server_ctx.pc,
262 Box::new(prev),
263 Box::new(map.clone()),
264 ));
265
266 ctx.ui.send(TheEvent::Custom(
267 TheId::named("Map Selection Changed"),
268 TheValue::Empty,
269 ));
270 }
271 server_ctx.profile_view = None;
272 }
273 }
274
275 undo_atom
276 }
277
278 fn draw_hud(
279 &mut self,
280 buffer: &mut TheRGBABuffer,
281 map: &mut Map,
282 ctx: &mut TheContext,
283 server_ctx: &mut ServerContext,
284 assets: &Assets,
285 ) {
286 self.hud.draw(buffer, map, ctx, server_ctx, None, assets);
287 }
288 }