1use crate::{editor::RUSTERIX, prelude::*};
2use MapEvent::*;
3use rusterix::{EntityAction, PlayerCamera, Value};
4use std::sync::Mutex;
5use theframework::prelude::*;
6
7pub struct GameTool {
8 id: TheId,
9
10 right: Option<Mutex<Box<TheCanvas>>>,
11 toolbar: Option<Mutex<Box<TheCanvas>>>,
12 editor_routed_prev_camera: Option<PlayerCamera>,
13}
14
15impl Tool for GameTool {
16 fn new() -> Self
17 where
18 Self: Sized,
19 {
20 Self {
21 id: TheId::named("Game Tool"),
22
23 right: None,
24 toolbar: None,
25 editor_routed_prev_camera: None,
26 }
27 }
28
29 fn id(&self) -> TheId {
30 self.id.clone()
31 }
32 fn info(&self) -> String {
33 fl!("tool_game")
34 }
35 fn icon_name(&self) -> String {
36 str!("joystick")
37 }
38 fn accel(&self) -> Option<char> {
39 Some('G')
40 }
41
42 fn help_url(&self) -> Option<String> {
43 Some("docs/creator/tools/overview".to_string())
44 }
45
46 fn tool_event(
47 &mut self,
48 tool_event: ToolEvent,
49 ui: &mut TheUI,
50 ctx: &mut TheContext,
51 project: &mut Project,
52 server_ctx: &mut ServerContext,
53 ) -> bool {
54 match tool_event {
55 ToolEvent::Activate => {
56 self.toolbar = None;
57 if let Some(layout) = ui.get_sharedvlayout("Shared VLayout") {
58 layout.set_mode(TheSharedVLayoutMode::Top);
59 if let Some(canvas) = layout.get_canvas_mut(0) {
60 if let Some(tool) = canvas.bottom.take() {
61 self.toolbar = Some(Mutex::new(tool));
62 }
63 }
64 }
65 server_ctx.curr_map_tool_type = MapToolType::Game;
66 server_ctx.game_mode = true;
67
68 if RUSTERIX.read().unwrap().server.state == rusterix::ServerState::Running {
72 let map_camera_to_player_camera = |mode: MapCamera| -> PlayerCamera {
73 match mode {
74 MapCamera::TwoD => PlayerCamera::D2,
75 MapCamera::ThreeDIso => PlayerCamera::D3Iso,
76 MapCamera::ThreeDFirstPerson => PlayerCamera::D3FirstP,
77 }
78 };
79 let restore_camera = if let Some(prev) = self.editor_routed_prev_camera.take() {
80 prev
81 } else {
82 let rusterix = RUSTERIX.read().unwrap();
83 let by_active_game_widget =
84 rusterix.client.active_game_widget_camera_mode();
85 let by_running_map = project
86 .regions
87 .iter()
88 .find(|r| r.map.name == rusterix.client.current_map)
89 .map(|r| map_camera_to_player_camera(r.map.camera));
90 let by_editor_region = project
91 .get_region(&server_ctx.curr_region)
92 .map(|r| map_camera_to_player_camera(r.map.camera));
93 by_active_game_widget
94 .or(by_running_map)
95 .or(by_editor_region)
96 .unwrap_or_else(|| rusterix.player_camera.clone())
97 };
98
99 RUSTERIX
100 .write()
101 .unwrap()
102 .server
103 .local_player_action(EntityAction::SetPlayerCamera(restore_camera));
104 } else {
105 self.editor_routed_prev_camera = None;
106 }
107
108 if let Some(right) = ui.canvas.right.take() {
109 self.right = Some(Mutex::new(right));
110 }
111 ctx.ui.redraw_all = true;
112 ctx.ui.relayout = true;
113
114 true
115 }
116 ToolEvent::DeActivate => {
117 ctx.set_cursor_visible(true);
118
119 if let Some(layout) = ui.get_sharedvlayout("Shared VLayout") {
120 layout.set_mode(TheSharedVLayoutMode::Shared);
121 if let Some(canvas) = layout.get_canvas_mut(0) {
122 if let Some(tool) = &mut self.toolbar {
123 let lock = tool.get_mut().unwrap();
124 let boxed_canvas: Box<TheCanvas> =
125 std::mem::replace(&mut *lock, Box::new(TheCanvas::default()));
126 canvas.bottom = Some(boxed_canvas);
127 }
128 }
129 }
130
131 if let Some(right) = &mut self.right {
132 let lock = right.get_mut().unwrap();
133 let boxed_canvas: Box<TheCanvas> =
134 std::mem::replace(&mut *lock, Box::new(TheCanvas::default()));
135 ui.canvas.right = Some(boxed_canvas);
136 ctx.ui.redraw_all = true;
137 ctx.ui.relayout = true;
138 }
139
140 server_ctx.game_mode = false;
141 true
142 }
143 _ => false,
144 }
145 }
146
147 fn map_event(
148 &mut self,
149 map_event: MapEvent,
150 _ui: &mut TheUI,
151 ctx: &mut TheContext,
152 map: &mut Map,
153 _server_ctx: &mut ServerContext,
154 ) -> Option<ProjectUndoAtom> {
155 match map_event {
156 MapClicked(coord) => {
157 let mut rusterix = RUSTERIX.write().unwrap();
158 let is_running = rusterix.server.state == rusterix::ServerState::Running;
159
160 if is_running {
161 if let Some(action) = rusterix.client.touch_down(coord, map) {
162 rusterix.server.local_player_action(action);
163 }
164 }
165 }
166 MapDragged(coord) => {
167 let mut rusterix = RUSTERIX.write().unwrap();
168 let is_running = rusterix.server.state == rusterix::ServerState::Running;
169
170 let is_inside = rusterix.client.is_inside_game(coord);
171 if is_running && is_inside {
172 ctx.set_cursor_visible(false);
173 rusterix.client_touch_dragged(coord, map);
174 } else {
175 ctx.set_cursor_visible(true);
176 }
177 }
178 MapUp(coord) => {
179 let mut rusterix = RUSTERIX.write().unwrap();
180 let is_running = rusterix.server.state == rusterix::ServerState::Running;
181
182 if is_running {
183 if let Some(action) = rusterix.client.touch_up(coord, map) {
184 rusterix.server.local_player_action(action);
185 }
186 rusterix.server.local_player_action(EntityAction::Off);
187 }
188 }
189 _ => {}
190 }
191
192 None
193 }
194
195 fn handle_event(
196 &mut self,
197 event: &TheEvent,
198 _ui: &mut TheUI,
199 ctx: &mut TheContext,
200 project: &mut Project,
201 server_ctx: &mut ServerContext,
202 ) -> bool {
203 let editor_view_to_player_camera = |mode: EditorViewMode| match mode {
204 EditorViewMode::D2 => PlayerCamera::D2,
205 EditorViewMode::FirstP => PlayerCamera::D3FirstP,
206 EditorViewMode::Iso | EditorViewMode::Orbit => PlayerCamera::D3Iso,
207 };
208
209 #[allow(clippy::single_match)]
210 match event {
211 TheEvent::KeyDown(TheValue::Char(char)) => {
212 let mut rusterix = crate::editor::RUSTERIX.write().unwrap();
213 if rusterix.server.state == rusterix::ServerState::Running {
214 if server_ctx.game_input_mode && !server_ctx.game_mode {
215 if self.editor_routed_prev_camera.is_none() {
218 self.editor_routed_prev_camera = Some(rusterix.player_camera.clone());
219 }
220 let camera = editor_view_to_player_camera(server_ctx.editor_view_mode);
221 rusterix
222 .server
223 .local_player_action(EntityAction::SetPlayerCamera(camera));
224
225 let action = rusterix
226 .client
227 .user_event("key_down".into(), Value::Str(char.to_string()));
228 rusterix.server.local_player_action(action);
229 } else {
230 let action = rusterix
231 .client
232 .user_event("key_down".into(), Value::Str(char.to_string()));
233
234 rusterix.server.local_player_action(action);
235 }
236 }
237 }
238 TheEvent::KeyUp(TheValue::Char(char)) => {
239 let mut rusterix = crate::editor::RUSTERIX.write().unwrap();
240 if rusterix.server.state == rusterix::ServerState::Running {
241 if server_ctx.game_input_mode && !server_ctx.game_mode {
242 let action = rusterix
243 .client
244 .user_event("key_up".into(), Value::Str(char.to_string()));
245 rusterix.server.local_player_action(action);
246 if let Some(prev) = self.editor_routed_prev_camera.take() {
247 rusterix
248 .server
249 .local_player_action(EntityAction::SetPlayerCamera(prev));
250 }
251 } else {
252 let action = rusterix
253 .client
254 .user_event("key_up".into(), Value::Str(char.to_string()));
255 rusterix.server.local_player_action(action);
256 }
257 }
258 }
259 TheEvent::RenderViewHoverChanged(id, coord) => {
260 if server_ctx.game_input_mode && !server_ctx.game_mode {
263 return false;
264 }
265 if id.name == "PolyView" {
266 let mut rusterix = RUSTERIX.write().unwrap();
267 let is_running = rusterix.server.state == rusterix::ServerState::Running;
268
269 let is_inside = rusterix.client.is_inside_game(*coord);
270 if is_running && is_inside {
271 ctx.set_cursor_visible(false);
272
273 for region in &project.regions {
274 if region.map.name == rusterix.client.current_map {
275 rusterix.client_touch_hover(*coord, ®ion.map);
276 break;
277 }
278 }
279 } else {
280 ctx.set_cursor_visible(true);
281 }
282 }
283 }
284 _ => {}
285 }
286
287 false
288 }
289}