1 /*****************************************************************************
2 * Copyright (c) 2014-2020 OpenRCT2 developers
3 *
4 * For a complete list of all authors, please refer to contributors.md
5 * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
6 *
7 * OpenRCT2 is licensed under the GNU General Public License version 3.
8 *****************************************************************************/
9
10 #include <algorithm>
11 #include <iterator>
12 #include <openrct2-ui/interface/LandTool.h>
13 #include <openrct2-ui/interface/Viewport.h>
14 #include <openrct2-ui/interface/Widget.h>
15 #include <openrct2-ui/windows/Window.h>
16 #include <openrct2/Cheats.h>
17 #include <openrct2/Context.h>
18 #include <openrct2/Game.h>
19 #include <openrct2/Input.h>
20 #include <openrct2/OpenRCT2.h>
21 #include <openrct2/actions/ChangeMapSizeAction.h>
22 #include <openrct2/actions/LandSetRightsAction.h>
23 #include <openrct2/actions/PlaceParkEntranceAction.h>
24 #include <openrct2/actions/PlacePeepSpawnAction.h>
25 #include <openrct2/actions/SurfaceSetStyleAction.h>
26 #include <openrct2/audio/audio.h>
27 #include <openrct2/localisation/Localisation.h>
28 #include <openrct2/peep/Staff.h>
29 #include <openrct2/ride/RideData.h>
30 #include <openrct2/ride/Track.h>
31 #include <openrct2/ride/TrainManager.h>
32 #include <openrct2/ride/Vehicle.h>
33 #include <openrct2/world/EntityList.h>
34 #include <openrct2/world/Entrance.h>
35 #include <openrct2/world/Footpath.h>
36 #include <openrct2/world/Scenery.h>
37 #include <openrct2/world/Sprite.h>
38 #include <openrct2/world/Surface.h>
39 #include <vector>
40
MapColour2(uint8_t colourA,uint8_t colourB)41 static constexpr uint16_t MapColour2(uint8_t colourA, uint8_t colourB)
42 {
43 return (colourA << 8) | colourB;
44 }
MapColour(uint8_t colour)45 static constexpr uint16_t MapColour(uint8_t colour)
46 {
47 return MapColour2(colour, colour);
48 }
MapColourUnowned(uint16_t colour)49 static constexpr uint16_t MapColourUnowned(uint16_t colour)
50 {
51 return MapColour2((colour & 0xFF00) >> 8, PALETTE_INDEX_10);
52 }
53
54 constexpr int32_t MAP_WINDOW_MAP_SIZE = MAXIMUM_MAP_SIZE_TECHNICAL * 2;
55
56 static constexpr const rct_string_id WINDOW_TITLE = STR_MAP_LABEL;
57 static constexpr const int32_t WH = 259;
58 static constexpr const int32_t WW = 245;
59
60 constexpr uint8_t DefaultPeepMapColour = PALETTE_INDEX_20;
61 constexpr uint8_t GuestMapColour = PALETTE_INDEX_172;
62 constexpr uint8_t GuestMapColourAlternate = PALETTE_INDEX_21;
63 constexpr uint8_t StaffMapColour = PALETTE_INDEX_138;
64 constexpr uint8_t StaffMapColourAlternate = PALETTE_INDEX_10;
65
66 // Some functions manipulate coordinates on the map. These are the coordinates of the pixels in the
67 // minimap. In order to distinguish those from actual coordinates, we use a separate name.
68 using MapCoordsXY = TileCoordsXY;
69
70 enum
71 {
72 PAGE_PEEPS,
73 PAGE_RIDES
74 };
75
76 enum WINDOW_MAP_WIDGET_IDX
77 {
78 WIDX_BACKGROUND,
79 WIDX_TITLE,
80 WIDX_CLOSE,
81 WIDX_RESIZE = 3,
82 WIDX_PEOPLE_TAB = 4,
83 WIDX_RIDES_TAB = 5,
84 WIDX_MAP = 6,
85 WIDX_MAP_SIZE_SPINNER = 7,
86 WIDX_MAP_SIZE_SPINNER_UP = 8,
87 WIDX_MAP_SIZE_SPINNER_DOWN = 9,
88 WIDX_SET_LAND_RIGHTS = 10,
89 WIDX_BUILD_PARK_ENTRANCE = 11,
90 WIDX_PEOPLE_STARTING_POSITION = 12,
91 WIDX_LAND_TOOL = 13,
92 WIDX_LAND_TOOL_SMALLER = 14,
93 WIDX_LAND_TOOL_LARGER = 15,
94 WIDX_LAND_OWNED_CHECKBOX = 16,
95 WIDX_CONSTRUCTION_RIGHTS_OWNED_CHECKBOX = 17,
96 WIDX_LAND_SALE_CHECKBOX = 18,
97 WIDX_CONSTRUCTION_RIGHTS_SALE_CHECKBOX = 19,
98 WIDX_ROTATE_90 = 20,
99 WIDX_MAP_GENERATOR = 21
100 };
101
102 validate_global_widx(WC_MAP, WIDX_ROTATE_90);
103
104 // clang-format off
105 static rct_widget window_map_widgets[] = {
106 WINDOW_SHIM(WINDOW_TITLE, WW, WH),
107 MakeWidget ({ 0, 43}, {245, 215}, WindowWidgetType::Resize, WindowColour::Secondary ),
108 MakeRemapWidget ({ 3, 17}, { 31, 27}, WindowWidgetType::ColourBtn, WindowColour::Secondary, SPR_TAB, STR_SHOW_PEOPLE_ON_MAP_TIP ),
109 MakeRemapWidget ({ 34, 17}, { 31, 27}, WindowWidgetType::ColourBtn, WindowColour::Secondary, SPR_TAB, STR_SHOW_RIDES_STALLS_ON_MAP_TIP ),
110 MakeWidget ({ 3, 46}, {239, 180}, WindowWidgetType::Scroll, WindowColour::Secondary, SCROLL_BOTH ),
111 MakeSpinnerWidgets({104, 229}, { 95, 12}, WindowWidgetType::Spinner, WindowColour::Secondary, STR_MAP_SIZE_VALUE ), // NB: 3 widgets
112 MakeWidget ({ 4, 1}, { 24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_BUY_LAND_RIGHTS, STR_SELECT_PARK_OWNED_LAND_TIP ),
113 MakeWidget ({ 4, 1}, { 24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_PARK_ENTRANCE, STR_BUILD_PARK_ENTRANCE_TIP ),
114 MakeWidget ({ 28, 1}, { 24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, 0xFFFFFFFF, STR_SET_STARTING_POSITIONS_TIP ),
115 MakeWidget ({ 4, 17}, { 44, 32}, WindowWidgetType::ImgBtn, WindowColour::Secondary, SPR_LAND_TOOL_SIZE_0 ),
116 MakeRemapWidget ({ 5, 18}, { 16, 16}, WindowWidgetType::TrnBtn, WindowColour::Secondary, SPR_LAND_TOOL_DECREASE, STR_ADJUST_SMALLER_LAND_TIP ),
117 MakeRemapWidget ({ 31, 32}, { 16, 16}, WindowWidgetType::TrnBtn, WindowColour::Secondary, SPR_LAND_TOOL_INCREASE, STR_ADJUST_LARGER_LAND_TIP ),
118 MakeWidget ({ 58, 197}, {184, 12}, WindowWidgetType::Checkbox, WindowColour::Secondary, STR_LAND_OWNED, STR_SET_LAND_TO_BE_OWNED_TIP ),
119 MakeWidget ({ 58, 197}, {184, 12}, WindowWidgetType::Checkbox, WindowColour::Secondary, STR_CONSTRUCTION_RIGHTS_OWNED, STR_SET_CONSTRUCTION_RIGHTS_TO_BE_OWNED_TIP ),
120 MakeWidget ({ 58, 197}, {184, 12}, WindowWidgetType::Checkbox, WindowColour::Secondary, STR_LAND_SALE, STR_SET_LAND_TO_BE_AVAILABLE_TIP ),
121 MakeWidget ({ 58, 197}, {174, 12}, WindowWidgetType::Checkbox, WindowColour::Secondary, STR_CONSTRUCTION_RIGHTS_SALE, STR_SET_CONSTRUCTION_RIGHTS_TO_BE_AVAILABLE_TIP),
122 MakeWidget ({218, 45}, { 24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_ROTATE_ARROW, STR_ROTATE_OBJECTS_90 ),
123 MakeWidget ({110, 189}, {131, 14}, WindowWidgetType::Button, WindowColour::Secondary, STR_MAPGEN_WINDOW_TITLE, STR_MAP_GENERATOR_TIP ),
124 WIDGETS_END,
125 };
126
127 // used in transforming viewport view coordinates to minimap coordinates
128 // rct2: 0x00981BBC
129 static constexpr const ScreenCoordsXY MiniMapOffsets[] = {
130 { MAXIMUM_MAP_SIZE_TECHNICAL - 8, 0 },
131 { 2 * MAXIMUM_MAP_SIZE_TECHNICAL - 8, MAXIMUM_MAP_SIZE_TECHNICAL },
132 { MAXIMUM_MAP_SIZE_TECHNICAL - 8, 2 * MAXIMUM_MAP_SIZE_TECHNICAL },
133 { 0 - 8, MAXIMUM_MAP_SIZE_TECHNICAL },
134 };
135 // clang-format on
136
137 /** rct2: 0x00981BCC */
138 static constexpr const uint16_t RideKeyColours[] = {
139 MapColour(PALETTE_INDEX_61), // COLOUR_KEY_RIDE
140 MapColour(PALETTE_INDEX_42), // COLOUR_KEY_FOOD
141 MapColour(PALETTE_INDEX_20), // COLOUR_KEY_DRINK
142 MapColour(PALETTE_INDEX_209), // COLOUR_KEY_SOUVENIR
143 MapColour(PALETTE_INDEX_136), // COLOUR_KEY_KIOSK
144 MapColour(PALETTE_INDEX_102), // COLOUR_KEY_FIRST_AID
145 MapColour(PALETTE_INDEX_55), // COLOUR_KEY_CASH_MACHINE
146 MapColour(PALETTE_INDEX_161), // COLOUR_KEY_TOILETS
147 };
148
149 static void window_map_close(rct_window* w);
150 static void window_map_resize(rct_window* w);
151 static void window_map_mouseup(rct_window* w, rct_widgetindex widgetIndex);
152 static void window_map_mousedown(rct_window* w, rct_widgetindex widgetIndex, rct_widget* widget);
153 static void window_map_update(rct_window* w);
154 static void window_map_toolupdate(rct_window* w, rct_widgetindex widgetIndex, const ScreenCoordsXY& screenCoords);
155 static void window_map_tooldown(rct_window* w, rct_widgetindex widgetIndex, const ScreenCoordsXY& screenCoords);
156 static void window_map_tooldrag(rct_window* w, rct_widgetindex widgetIndex, const ScreenCoordsXY& screenCoords);
157 static void window_map_toolabort(rct_window* w, rct_widgetindex widgetIndex);
158 static void window_map_scrollgetsize(rct_window* w, int32_t scrollIndex, int32_t* width, int32_t* height);
159 static void window_map_scrollmousedown(rct_window* w, int32_t scrollIndex, const ScreenCoordsXY& screenCoords);
160 static void window_map_textinput(rct_window* w, rct_widgetindex widgetIndex, char* text);
161 static void window_map_invalidate(rct_window* w);
162 static void window_map_paint(rct_window* w, rct_drawpixelinfo* dpi);
163 static void window_map_scrollpaint(rct_window* w, rct_drawpixelinfo* dpi, int32_t scrollIndex);
164
__anonc7b917650202(auto& events) 165 static rct_window_event_list window_map_events([](auto& events) {
166 events.close = &window_map_close;
167 events.mouse_up = &window_map_mouseup;
168 events.resize = &window_map_resize;
169 events.mouse_down = &window_map_mousedown;
170 events.update = &window_map_update;
171 events.tool_update = &window_map_toolupdate;
172 events.tool_down = &window_map_tooldown;
173 events.tool_drag = &window_map_tooldrag;
174 events.tool_abort = &window_map_toolabort;
175 events.get_scroll_size = &window_map_scrollgetsize;
176 events.scroll_mousedown = &window_map_scrollmousedown;
177 events.scroll_mousedrag = &window_map_scrollmousedown;
178 events.text_input = &window_map_textinput;
179 events.invalidate = &window_map_invalidate;
180 events.paint = &window_map_paint;
181 events.scroll_paint = &window_map_scrollpaint;
182 });
183
184 /** rct2: 0x00F1AD61 */
185 static uint8_t _activeTool;
186
187 /** rct2: 0x00F1AD6C */
188 static uint32_t _currentLine;
189
190 /** rct2: 0x00F1AD68 */
191 static std::vector<uint8_t> _mapImageData;
192
193 static uint16_t _landRightsToolSize;
194
195 static void window_map_init_map();
196 static void window_map_centre_on_view_point();
197 static void window_map_show_default_scenario_editor_buttons(rct_window* w);
198 static void window_map_draw_tab_images(rct_window* w, rct_drawpixelinfo* dpi);
199 static void window_map_paint_peep_overlay(rct_drawpixelinfo* dpi);
200 static void window_map_paint_train_overlay(rct_drawpixelinfo* dpi);
201 static void window_map_paint_hud_rectangle(rct_drawpixelinfo* dpi);
202 static void window_map_inputsize_land(rct_window* w);
203 static void window_map_inputsize_map(rct_window* w);
204
205 static void window_map_set_land_rights_tool_update(const ScreenCoordsXY& screenCoords);
206 static void window_map_place_park_entrance_tool_update(const ScreenCoordsXY& screenCoords);
207 static void window_map_set_peep_spawn_tool_update(const ScreenCoordsXY& screenCoords);
208 static void window_map_place_park_entrance_tool_down(const ScreenCoordsXY& screenCoords);
209 static void window_map_set_peep_spawn_tool_down(const ScreenCoordsXY& screenCoords);
210 static void map_window_increase_map_size();
211 static void map_window_decrease_map_size();
212 static void map_window_set_pixels(rct_window* w);
213
214 static CoordsXY map_window_screen_to_map(ScreenCoordsXY screenCoords);
215
216 /**
217 *
218 * rct2: 0x0068C88A
219 */
window_map_open()220 rct_window* window_map_open()
221 {
222 rct_window* w;
223
224 // Check if window is already open
225 w = window_bring_to_front_by_class(WC_MAP);
226 if (w != nullptr)
227 {
228 w->selected_tab = 0;
229 w->list_information_type = 0;
230 return w;
231 }
232
233 try
234 {
235 _mapImageData.resize(MAP_WINDOW_MAP_SIZE * MAP_WINDOW_MAP_SIZE);
236 }
237 catch (const std::bad_alloc&)
238 {
239 return nullptr;
240 }
241
242 w = WindowCreateAutoPos(245, 259, &window_map_events, WC_MAP, WF_10);
243 w->widgets = window_map_widgets;
244 w->enabled_widgets = (1ULL << WIDX_CLOSE) | (1ULL << WIDX_PEOPLE_TAB) | (1ULL << WIDX_RIDES_TAB)
245 | (1ULL << WIDX_MAP_SIZE_SPINNER) | (1ULL << WIDX_MAP_SIZE_SPINNER_UP) | (1ULL << WIDX_MAP_SIZE_SPINNER_DOWN)
246 | (1ULL << WIDX_LAND_TOOL) | (1ULL << WIDX_LAND_TOOL_SMALLER) | (1ULL << WIDX_LAND_TOOL_LARGER)
247 | (1ULL << WIDX_SET_LAND_RIGHTS) | (1ULL << WIDX_LAND_OWNED_CHECKBOX)
248 | (1ULL << WIDX_CONSTRUCTION_RIGHTS_OWNED_CHECKBOX) | (1ULL << WIDX_LAND_SALE_CHECKBOX)
249 | (1ULL << WIDX_CONSTRUCTION_RIGHTS_SALE_CHECKBOX) | (1ULL << WIDX_BUILD_PARK_ENTRANCE) | (1ULL << WIDX_ROTATE_90)
250 | (1ULL << WIDX_PEOPLE_STARTING_POSITION) | (1ULL << WIDX_MAP_GENERATOR);
251
252 w->hold_down_widgets = (1ULL << WIDX_MAP_SIZE_SPINNER_UP) | (1ULL << WIDX_MAP_SIZE_SPINNER_DOWN)
253 | (1ULL << WIDX_LAND_TOOL_LARGER) | (1ULL << WIDX_LAND_TOOL_SMALLER);
254
255 WindowInitScrollWidgets(w);
256
257 w->map.rotation = get_current_rotation();
258
259 window_map_init_map();
260 gWindowSceneryRotation = 0;
261 window_map_centre_on_view_point();
262
263 // Reset land rights tool size
264 _landRightsToolSize = 1;
265
266 return w;
267 }
268
window_map_reset()269 void window_map_reset()
270 {
271 rct_window* w;
272
273 // Check if window is even opened
274 w = window_bring_to_front_by_class(WC_MAP);
275 if (w == nullptr)
276 {
277 return;
278 }
279
280 window_map_init_map();
281 window_map_centre_on_view_point();
282 }
283
284 /**
285 *
286 * rct2: 0x0068D0F1
287 */
window_map_close(rct_window * w)288 static void window_map_close(rct_window* w)
289 {
290 _mapImageData.clear();
291 _mapImageData.shrink_to_fit();
292 if ((input_test_flag(INPUT_FLAG_TOOL_ACTIVE)) && gCurrentToolWidget.window_classification == w->classification
293 && gCurrentToolWidget.window_number == w->number)
294 {
295 tool_cancel();
296 }
297 }
298
299 /**
300 *
301 * rct2: 0x0068CFC1
302 */
window_map_mouseup(rct_window * w,rct_widgetindex widgetIndex)303 static void window_map_mouseup(rct_window* w, rct_widgetindex widgetIndex)
304 {
305 switch (widgetIndex)
306 {
307 case WIDX_CLOSE:
308 window_close(w);
309 break;
310 case WIDX_SET_LAND_RIGHTS:
311 w->Invalidate();
312 if (tool_set(w, widgetIndex, Tool::UpArrow))
313 break;
314 _activeTool = 2;
315 // Prevent mountain tool size.
316 _landRightsToolSize = std::max<uint16_t>(MINIMUM_TOOL_SIZE, _landRightsToolSize);
317 show_gridlines();
318 show_land_rights();
319 show_construction_rights();
320 break;
321 case WIDX_LAND_OWNED_CHECKBOX:
322 _activeTool ^= 2;
323
324 if (_activeTool & 2)
325 _activeTool &= 0xF2;
326
327 w->Invalidate();
328 break;
329 case WIDX_LAND_SALE_CHECKBOX:
330 _activeTool ^= 8;
331
332 if (_activeTool & 8)
333 _activeTool &= 0xF8;
334
335 w->Invalidate();
336 break;
337 case WIDX_CONSTRUCTION_RIGHTS_OWNED_CHECKBOX:
338 _activeTool ^= 1;
339
340 if (_activeTool & 1)
341 _activeTool &= 0xF1;
342
343 w->Invalidate();
344 break;
345 case WIDX_CONSTRUCTION_RIGHTS_SALE_CHECKBOX:
346 _activeTool ^= 4;
347
348 if (_activeTool & 4)
349 _activeTool &= 0xF4;
350
351 w->Invalidate();
352 break;
353 case WIDX_BUILD_PARK_ENTRANCE:
354 w->Invalidate();
355 if (tool_set(w, widgetIndex, Tool::UpArrow))
356 break;
357
358 gParkEntranceGhostExists = false;
359 input_set_flag(INPUT_FLAG_6, true);
360
361 show_gridlines();
362 show_land_rights();
363 show_construction_rights();
364 break;
365 case WIDX_ROTATE_90:
366 gWindowSceneryRotation = (gWindowSceneryRotation + 1) & 3;
367 break;
368 case WIDX_PEOPLE_STARTING_POSITION:
369 if (tool_set(w, widgetIndex, Tool::UpArrow))
370 break;
371
372 show_gridlines();
373 show_land_rights();
374 show_construction_rights();
375 break;
376 case WIDX_LAND_TOOL:
377 window_map_inputsize_land(w);
378 break;
379 case WIDX_MAP_SIZE_SPINNER:
380 window_map_inputsize_map(w);
381 break;
382 case WIDX_MAP_GENERATOR:
383 context_open_window(WC_MAPGEN);
384 break;
385 default:
386 if (widgetIndex >= WIDX_PEOPLE_TAB && widgetIndex <= WIDX_RIDES_TAB)
387 {
388 widgetIndex -= WIDX_PEOPLE_TAB;
389 if (widgetIndex == w->selected_tab)
390 break;
391
392 w->selected_tab = widgetIndex;
393 w->list_information_type = 0;
394 }
395 }
396 }
397
398 /**
399 *
400 * rct2: 0x0068D7DC
401 */
window_map_resize(rct_window * w)402 static void window_map_resize(rct_window* w)
403 {
404 w->flags |= WF_RESIZABLE;
405 w->min_width = 245;
406 w->max_width = 800;
407 w->min_height = 259;
408 w->max_height = 560;
409 }
410
411 /**
412 *
413 * rct2: 0x0068D040
414 */
window_map_mousedown(rct_window * w,rct_widgetindex widgetIndex,rct_widget * widget)415 static void window_map_mousedown(rct_window* w, rct_widgetindex widgetIndex, rct_widget* widget)
416 {
417 switch (widgetIndex)
418 {
419 case WIDX_MAP_SIZE_SPINNER_UP:
420 map_window_increase_map_size();
421 break;
422 case WIDX_MAP_SIZE_SPINNER_DOWN:
423 map_window_decrease_map_size();
424 break;
425 case WIDX_LAND_TOOL_SMALLER:
426 // Decrement land rights tool size
427 _landRightsToolSize = std::max(MINIMUM_TOOL_SIZE, _landRightsToolSize - 1);
428
429 w->Invalidate();
430 break;
431 case WIDX_LAND_TOOL_LARGER:
432 // Increment land rights tool size
433 _landRightsToolSize = std::min(MAXIMUM_TOOL_SIZE, _landRightsToolSize + 1);
434
435 w->Invalidate();
436 break;
437 }
438 }
439
440 /**
441 *
442 * rct2: 0x0068D7FB
443 */
window_map_update(rct_window * w)444 static void window_map_update(rct_window* w)
445 {
446 if (get_current_rotation() != w->map.rotation)
447 {
448 w->map.rotation = get_current_rotation();
449 window_map_init_map();
450 window_map_centre_on_view_point();
451 }
452
453 for (int32_t i = 0; i < 16; i++)
454 map_window_set_pixels(w);
455
456 w->Invalidate();
457
458 // Update tab animations
459 w->list_information_type++;
460 switch (w->selected_tab)
461 {
462 case PAGE_PEEPS:
463 if (w->list_information_type >= 32)
464 {
465 w->list_information_type = 0;
466 }
467 break;
468 case PAGE_RIDES:
469 if (w->list_information_type >= 64)
470 {
471 w->list_information_type = 0;
472 }
473 break;
474 }
475 }
476
477 /**
478 *
479 * rct2: 0x0068D093
480 */
window_map_toolupdate(rct_window * w,rct_widgetindex widgetIndex,const ScreenCoordsXY & screenCoords)481 static void window_map_toolupdate(rct_window* w, rct_widgetindex widgetIndex, const ScreenCoordsXY& screenCoords)
482 {
483 switch (widgetIndex)
484 {
485 case WIDX_SET_LAND_RIGHTS:
486 window_map_set_land_rights_tool_update(screenCoords);
487 break;
488 case WIDX_BUILD_PARK_ENTRANCE:
489 window_map_place_park_entrance_tool_update(screenCoords);
490 break;
491 case WIDX_PEOPLE_STARTING_POSITION:
492 window_map_set_peep_spawn_tool_update(screenCoords);
493 break;
494 }
495 }
496
497 /**
498 *
499 * rct2: 0x0068D074
500 */
window_map_tooldown(rct_window * w,rct_widgetindex widgetIndex,const ScreenCoordsXY & screenCoords)501 static void window_map_tooldown(rct_window* w, rct_widgetindex widgetIndex, const ScreenCoordsXY& screenCoords)
502 {
503 switch (widgetIndex)
504 {
505 case WIDX_BUILD_PARK_ENTRANCE:
506 window_map_place_park_entrance_tool_down(screenCoords);
507 break;
508 case WIDX_PEOPLE_STARTING_POSITION:
509 window_map_set_peep_spawn_tool_down(screenCoords);
510 break;
511 }
512 }
513
514 /**
515 *
516 * rct2: 0x0068D088
517 */
window_map_tooldrag(rct_window * w,rct_widgetindex widgetIndex,const ScreenCoordsXY & screenCoords)518 static void window_map_tooldrag(rct_window* w, rct_widgetindex widgetIndex, const ScreenCoordsXY& screenCoords)
519 {
520 switch (widgetIndex)
521 {
522 case WIDX_SET_LAND_RIGHTS:
523 if (gMapSelectFlags & MAP_SELECT_FLAG_ENABLE)
524 {
525 auto landSetRightsAction = LandSetRightsAction(
526 { gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y },
527 LandSetRightSetting::SetOwnershipWithChecks, _activeTool << 4);
528 GameActions::Execute(&landSetRightsAction);
529 }
530 break;
531 }
532 }
533
534 /**
535 *
536 * rct2: 0x0068D055
537 */
window_map_toolabort(rct_window * w,rct_widgetindex widgetIndex)538 static void window_map_toolabort(rct_window* w, rct_widgetindex widgetIndex)
539 {
540 switch (widgetIndex)
541 {
542 case WIDX_SET_LAND_RIGHTS:
543 w->Invalidate();
544 hide_gridlines();
545 hide_land_rights();
546 hide_construction_rights();
547 break;
548 case WIDX_BUILD_PARK_ENTRANCE:
549 park_entrance_remove_ghost();
550 w->Invalidate();
551 hide_gridlines();
552 hide_land_rights();
553 hide_construction_rights();
554 break;
555 case WIDX_PEOPLE_STARTING_POSITION:
556 w->Invalidate();
557 hide_gridlines();
558 hide_land_rights();
559 hide_construction_rights();
560 break;
561 }
562 }
563
564 /**
565 *
566 * rct2: 0x0068D7CC
567 */
window_map_scrollgetsize(rct_window * w,int32_t scrollIndex,int32_t * width,int32_t * height)568 static void window_map_scrollgetsize(rct_window* w, int32_t scrollIndex, int32_t* width, int32_t* height)
569 {
570 window_map_invalidate(w);
571
572 *width = MAP_WINDOW_MAP_SIZE;
573 *height = MAP_WINDOW_MAP_SIZE;
574 }
575
576 /**
577 *
578 * rct2: 0x0068D726
579 */
window_map_scrollmousedown(rct_window * w,int32_t scrollIndex,const ScreenCoordsXY & screenCoords)580 static void window_map_scrollmousedown(rct_window* w, int32_t scrollIndex, const ScreenCoordsXY& screenCoords)
581 {
582 CoordsXY c = map_window_screen_to_map(screenCoords);
583 auto mapCoords = CoordsXY{ std::clamp(c.x, 0, MAXIMUM_MAP_SIZE_BIG - 1), std::clamp(c.y, 0, MAXIMUM_MAP_SIZE_BIG - 1) };
584 auto mapZ = tile_element_height(mapCoords);
585
586 rct_window* mainWindow = window_get_main();
587 if (mainWindow != nullptr)
588 {
589 window_scroll_to_location(mainWindow, { mapCoords, mapZ });
590 }
591
592 if (land_tool_is_active())
593 {
594 // Set land terrain
595 int32_t landToolSize = std::max<int32_t>(1, gLandToolSize);
596 int32_t size = (landToolSize * 32) - 32;
597 int32_t radius = (landToolSize * 16) - 16;
598
599 mapCoords = (mapCoords - CoordsXY{ radius, radius }).ToTileStart();
600 map_invalidate_selection_rect();
601 gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE;
602 gMapSelectType = MAP_SELECT_TYPE_FULL;
603 gMapSelectPositionA = mapCoords;
604 gMapSelectPositionB = mapCoords + CoordsXY{ size, size };
605 map_invalidate_selection_rect();
606
607 auto surfaceSetStyleAction = SurfaceSetStyleAction(
608 { gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y },
609 gLandToolTerrainSurface, gLandToolTerrainEdge);
610 GameActions::Execute(&surfaceSetStyleAction);
611 }
612 else if (WidgetIsActiveTool(w, WIDX_SET_LAND_RIGHTS))
613 {
614 // Set land rights
615 int32_t landRightsToolSize = std::max<int32_t>(1, _landRightsToolSize);
616 int32_t size = (landRightsToolSize * 32) - 32;
617 int32_t radius = (landRightsToolSize * 16) - 16;
618 mapCoords = (mapCoords - CoordsXY{ radius, radius }).ToTileStart();
619
620 map_invalidate_selection_rect();
621 gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE;
622 gMapSelectType = MAP_SELECT_TYPE_FULL;
623 gMapSelectPositionA = mapCoords;
624 gMapSelectPositionB = mapCoords + CoordsXY{ size, size };
625 map_invalidate_selection_rect();
626
627 auto landSetRightsAction = LandSetRightsAction(
628 { gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y },
629 LandSetRightSetting::SetOwnershipWithChecks, _activeTool << 4);
630 GameActions::Execute(&landSetRightsAction);
631 }
632 }
633
window_map_textinput(rct_window * w,rct_widgetindex widgetIndex,char * text)634 static void window_map_textinput(rct_window* w, rct_widgetindex widgetIndex, char* text)
635 {
636 int32_t size;
637 char* end;
638
639 if (text == nullptr)
640 return;
641
642 switch (widgetIndex)
643 {
644 case WIDX_LAND_TOOL:
645 size = strtol(text, &end, 10);
646 if (*end == '\0')
647 {
648 size = std::clamp(size, MINIMUM_TOOL_SIZE, MAXIMUM_TOOL_SIZE);
649 _landRightsToolSize = size;
650 w->Invalidate();
651 }
652 break;
653 case WIDX_MAP_SIZE_SPINNER:
654 size = strtol(text, &end, 10);
655 if (*end == '\0')
656 {
657 // The practical size is 2 lower than the technical size
658 size += 2;
659 size = std::clamp(size, MINIMUM_MAP_SIZE_TECHNICAL, MAXIMUM_MAP_SIZE_TECHNICAL);
660
661 auto changeMapSizeAction = ChangeMapSizeAction(size);
662 GameActions::Execute(&changeMapSizeAction);
663 w->Invalidate();
664 }
665 break;
666 }
667 }
668
669 /**
670 *
671 * rct2: 0x0068CA8F
672 */
window_map_invalidate(rct_window * w)673 static void window_map_invalidate(rct_window* w)
674 {
675 uint64_t pressedWidgets;
676 int32_t i, height;
677
678 // Set the pressed widgets
679 pressedWidgets = w->pressed_widgets;
680 pressedWidgets &= (1ULL << WIDX_PEOPLE_TAB);
681 pressedWidgets &= (1ULL << WIDX_RIDES_TAB);
682 pressedWidgets &= (1ULL << WIDX_MAP);
683 pressedWidgets &= (1ULL << WIDX_LAND_OWNED_CHECKBOX);
684 pressedWidgets &= (1ULL << WIDX_CONSTRUCTION_RIGHTS_OWNED_CHECKBOX);
685 pressedWidgets &= (1ULL << WIDX_LAND_SALE_CHECKBOX);
686 pressedWidgets &= (1ULL << WIDX_CONSTRUCTION_RIGHTS_SALE_CHECKBOX);
687
688 pressedWidgets |= (1ULL << (WIDX_PEOPLE_TAB + w->selected_tab));
689 pressedWidgets |= (1ULL << WIDX_LAND_TOOL);
690
691 if (_activeTool & (1 << 3))
692 pressedWidgets |= (1ULL << WIDX_LAND_SALE_CHECKBOX);
693
694 if (_activeTool & (1 << 2))
695 pressedWidgets |= (1ULL << WIDX_CONSTRUCTION_RIGHTS_SALE_CHECKBOX);
696
697 if (_activeTool & (1 << 1))
698 pressedWidgets |= (1ULL << WIDX_LAND_OWNED_CHECKBOX);
699
700 if (_activeTool & (1 << 0))
701 pressedWidgets |= (1ULL << WIDX_CONSTRUCTION_RIGHTS_OWNED_CHECKBOX);
702
703 w->pressed_widgets = pressedWidgets;
704
705 // Resize widgets to window size
706 w->widgets[WIDX_BACKGROUND].right = w->width - 1;
707 w->widgets[WIDX_BACKGROUND].bottom = w->height - 1;
708 w->widgets[WIDX_RESIZE].right = w->width - 1;
709 w->widgets[WIDX_RESIZE].bottom = w->height - 1;
710 w->widgets[WIDX_TITLE].right = w->width - 2;
711 w->widgets[WIDX_CLOSE].left = w->width - 2 - 11;
712 w->widgets[WIDX_CLOSE].right = w->width - 2 - 11 + 10;
713 w->widgets[WIDX_MAP].right = w->width - 4;
714
715 if ((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) || gCheatsSandboxMode)
716 w->widgets[WIDX_MAP].bottom = w->height - 1 - 72;
717 else if (w->selected_tab == PAGE_RIDES)
718 w->widgets[WIDX_MAP].bottom = w->height - 1 - (4 * LIST_ROW_HEIGHT + 4);
719 else
720 w->widgets[WIDX_MAP].bottom = w->height - 1 - 14;
721
722 w->widgets[WIDX_MAP_SIZE_SPINNER].top = w->height - 15;
723 w->widgets[WIDX_MAP_SIZE_SPINNER].bottom = w->height - 4;
724 w->widgets[WIDX_MAP_SIZE_SPINNER_UP].top = w->height - 14;
725 w->widgets[WIDX_MAP_SIZE_SPINNER_UP].bottom = w->height - 5;
726 w->widgets[WIDX_MAP_SIZE_SPINNER_DOWN].top = w->height - 14;
727 w->widgets[WIDX_MAP_SIZE_SPINNER_DOWN].bottom = w->height - 5;
728
729 w->widgets[WIDX_SET_LAND_RIGHTS].top = w->height - 70;
730 w->widgets[WIDX_SET_LAND_RIGHTS].bottom = w->height - 70 + 23;
731 w->widgets[WIDX_BUILD_PARK_ENTRANCE].top = w->height - 46;
732 w->widgets[WIDX_BUILD_PARK_ENTRANCE].bottom = w->height - 46 + 23;
733 w->widgets[WIDX_ROTATE_90].top = w->height - 46;
734 w->widgets[WIDX_ROTATE_90].bottom = w->height - 46 + 23;
735 w->widgets[WIDX_PEOPLE_STARTING_POSITION].top = w->height - 46;
736 w->widgets[WIDX_PEOPLE_STARTING_POSITION].bottom = w->height - 46 + 23;
737
738 w->widgets[WIDX_LAND_TOOL].top = w->height - 42;
739 w->widgets[WIDX_LAND_TOOL].bottom = w->height - 42 + 30;
740 w->widgets[WIDX_LAND_TOOL_SMALLER].top = w->height - 41;
741 w->widgets[WIDX_LAND_TOOL_SMALLER].bottom = w->height - 41 + 15;
742 w->widgets[WIDX_LAND_TOOL_LARGER].top = w->height - 27;
743 w->widgets[WIDX_LAND_TOOL_LARGER].bottom = w->height - 27 + 15;
744
745 w->widgets[WIDX_MAP_GENERATOR].top = w->height - 69;
746 w->widgets[WIDX_MAP_GENERATOR].bottom = w->height - 69 + 13;
747
748 // Land tool mode (4 checkboxes)
749 height = w->height - 55;
750 for (i = 0; i < 4; i++)
751 {
752 w->widgets[WIDX_LAND_OWNED_CHECKBOX + i].top = height;
753 height += 11;
754 w->widgets[WIDX_LAND_OWNED_CHECKBOX + i].bottom = height;
755 height += 2;
756 }
757
758 // Disable all scenario editor related widgets
759 for (i = WIDX_MAP_SIZE_SPINNER; i <= WIDX_MAP_GENERATOR; i++)
760 {
761 w->widgets[i].type = WindowWidgetType::Empty;
762 }
763
764 if ((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) || gCheatsSandboxMode)
765 {
766 // scenario editor: build park entrance selected, show rotate button
767 if ((input_test_flag(INPUT_FLAG_TOOL_ACTIVE)) && gCurrentToolWidget.window_classification == WC_MAP
768 && gCurrentToolWidget.widget_index == WIDX_BUILD_PARK_ENTRANCE)
769 {
770 w->widgets[WIDX_ROTATE_90].type = WindowWidgetType::FlatBtn;
771 }
772
773 // Always show set land rights button
774 w->widgets[WIDX_SET_LAND_RIGHTS].type = WindowWidgetType::FlatBtn;
775
776 // If any tool is active
777 if ((input_test_flag(INPUT_FLAG_TOOL_ACTIVE)) && gCurrentToolWidget.window_classification == WC_MAP)
778 {
779 // if not in set land rights mode: show the default scenario editor buttons
780 if (gCurrentToolWidget.widget_index != WIDX_SET_LAND_RIGHTS)
781 {
782 window_map_show_default_scenario_editor_buttons(w);
783 }
784 else
785 { // if in set land rights mode: show land tool buttons + modes
786 w->widgets[WIDX_LAND_TOOL].type = WindowWidgetType::ImgBtn;
787 w->widgets[WIDX_LAND_TOOL_SMALLER].type = WindowWidgetType::TrnBtn;
788 w->widgets[WIDX_LAND_TOOL_LARGER].type = WindowWidgetType::TrnBtn;
789
790 for (i = 0; i < 4; i++)
791 w->widgets[WIDX_LAND_OWNED_CHECKBOX + i].type = WindowWidgetType::Checkbox;
792
793 w->widgets[WIDX_LAND_TOOL].image = LandTool::SizeToSpriteIndex(_landRightsToolSize);
794 }
795 }
796 else
797 {
798 // if no tool is active: show the default scenario editor buttons
799 window_map_show_default_scenario_editor_buttons(w);
800 }
801 }
802 }
803
804 /**
805 *
806 * rct2: 0x0068CDA9
807 */
window_map_paint(rct_window * w,rct_drawpixelinfo * dpi)808 static void window_map_paint(rct_window* w, rct_drawpixelinfo* dpi)
809 {
810 WindowDrawWidgets(w, dpi);
811 window_map_draw_tab_images(w, dpi);
812
813 auto screenCoords = w->windowPos
814 + ScreenCoordsXY{ window_map_widgets[WIDX_LAND_TOOL].midX(), window_map_widgets[WIDX_LAND_TOOL].midY() };
815
816 // Draw land tool size
817 if (WidgetIsActiveTool(w, WIDX_SET_LAND_RIGHTS) && _landRightsToolSize > MAX_TOOL_SIZE_WITH_SPRITE)
818 {
819 auto ft = Formatter();
820 ft.Add<uint16_t>(_landRightsToolSize);
821 DrawTextBasic(dpi, screenCoords - ScreenCoordsXY{ 0, 2 }, STR_LAND_TOOL_SIZE_VALUE, ft, { TextAlignment::CENTRE });
822 }
823 screenCoords.y = w->windowPos.y + window_map_widgets[WIDX_LAND_TOOL].bottom + 5;
824
825 // People starting position (scenario editor only)
826 if (w->widgets[WIDX_PEOPLE_STARTING_POSITION].type != WindowWidgetType::Empty)
827 {
828 screenCoords = w->windowPos
829 + ScreenCoordsXY{ w->widgets[WIDX_PEOPLE_STARTING_POSITION].left + 12,
830 w->widgets[WIDX_PEOPLE_STARTING_POSITION].top + 18 };
831 gfx_draw_sprite(dpi, ImageId(SPR_6410, COLOUR_BRIGHT_RED, COLOUR_LIGHT_BROWN), screenCoords);
832 }
833
834 if (!(gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && !gCheatsSandboxMode)
835 {
836 // Render the map legend
837 if (w->selected_tab == PAGE_RIDES)
838 {
839 screenCoords = w->windowPos + ScreenCoordsXY{ 4, w->widgets[WIDX_MAP].bottom + 2 };
840
841 static rct_string_id mapLabels[] = {
842 STR_MAP_RIDE, STR_MAP_FOOD_STALL, STR_MAP_DRINK_STALL, STR_MAP_SOUVENIR_STALL,
843 STR_MAP_INFO_KIOSK, STR_MAP_FIRST_AID, STR_MAP_CASH_MACHINE, STR_MAP_TOILET,
844 };
845
846 for (uint32_t i = 0; i < std::size(RideKeyColours); i++)
847 {
848 gfx_fill_rect(
849 dpi, { screenCoords + ScreenCoordsXY{ 0, 2 }, screenCoords + ScreenCoordsXY{ 6, 8 } }, RideKeyColours[i]);
850 DrawTextBasic(dpi, screenCoords + ScreenCoordsXY{ LIST_ROW_HEIGHT, 0 }, mapLabels[i], {});
851 screenCoords.y += LIST_ROW_HEIGHT;
852 if (i == 3)
853 {
854 screenCoords += { 118, -(LIST_ROW_HEIGHT * 4) };
855 }
856 }
857 }
858 }
859 else if (!WidgetIsActiveTool(w, WIDX_SET_LAND_RIGHTS))
860 {
861 DrawTextBasic(
862 dpi, w->windowPos + ScreenCoordsXY{ 4, w->widgets[WIDX_MAP_SIZE_SPINNER].top + 1 }, STR_MAP_SIZE, {},
863 { w->colours[1] });
864 }
865 }
866
867 /**
868 *
869 * rct2: 0x0068CF23
870 */
window_map_scrollpaint(rct_window * w,rct_drawpixelinfo * dpi,int32_t scrollIndex)871 static void window_map_scrollpaint(rct_window* w, rct_drawpixelinfo* dpi, int32_t scrollIndex)
872 {
873 gfx_clear(dpi, PALETTE_INDEX_10);
874
875 rct_g1_element g1temp = {};
876 g1temp.offset = _mapImageData.data();
877 g1temp.width = MAP_WINDOW_MAP_SIZE;
878 g1temp.height = MAP_WINDOW_MAP_SIZE;
879 g1temp.x_offset = -8;
880 g1temp.y_offset = -8;
881 gfx_set_g1_element(SPR_TEMP, &g1temp);
882 drawing_engine_invalidate_image(SPR_TEMP);
883 gfx_draw_sprite(dpi, ImageId(SPR_TEMP), { 0, 0 });
884
885 if (w->selected_tab == PAGE_PEEPS)
886 {
887 window_map_paint_peep_overlay(dpi);
888 }
889 else
890 {
891 window_map_paint_train_overlay(dpi);
892 }
893 window_map_paint_hud_rectangle(dpi);
894 }
895
896 /**
897 *
898 * rct2: 0x0068CA6C
899 */
window_map_init_map()900 static void window_map_init_map()
901 {
902 std::fill(_mapImageData.begin(), _mapImageData.end(), PALETTE_INDEX_10);
903 _currentLine = 0;
904 }
905
906 /**
907 *
908 * rct2: 0x0068C990
909 */
window_map_centre_on_view_point()910 static void window_map_centre_on_view_point()
911 {
912 rct_window* w = window_get_main();
913 rct_window* w_map;
914 int16_t ax, bx, cx, dx;
915 int16_t bp, di;
916
917 if (w == nullptr || w->viewport == nullptr)
918 return;
919
920 w_map = window_find_by_class(WC_MAP);
921 if (w_map == nullptr)
922 return;
923
924 auto offset = MiniMapOffsets[get_current_rotation()];
925
926 // calculate centre view point of viewport and transform it to minimap coordinates
927
928 cx = ((w->viewport->view_width >> 1) + w->viewport->viewPos.x) >> 5;
929 dx = ((w->viewport->view_height >> 1) + w->viewport->viewPos.y) >> 4;
930 cx += offset.x;
931 dx += offset.y;
932
933 // calculate width and height of minimap
934
935 ax = w_map->widgets[WIDX_MAP].width() - 11;
936 bx = w_map->widgets[WIDX_MAP].height() - 11;
937 bp = ax;
938 di = bx;
939
940 ax >>= 1;
941 bx >>= 1;
942 cx = std::max(cx - ax, 0);
943 dx = std::max(dx - bx, 0);
944
945 bp = w_map->scrolls[0].h_right - bp;
946 di = w_map->scrolls[0].v_bottom - di;
947
948 if (bp < 0 && (bp - cx) < 0)
949 cx = 0;
950
951 if (di < 0 && (di - dx) < 0)
952 dx = 0;
953
954 w_map->scrolls[0].h_left = cx;
955 w_map->scrolls[0].v_top = dx;
956 WidgetScrollUpdateThumbs(w_map, WIDX_MAP);
957 }
958
959 /**
960 *
961 * rct2: 0x0068CD35 (part of 0x0068CA8F)
962 */
window_map_show_default_scenario_editor_buttons(rct_window * w)963 static void window_map_show_default_scenario_editor_buttons(rct_window* w)
964 {
965 w->widgets[WIDX_BUILD_PARK_ENTRANCE].type = WindowWidgetType::FlatBtn;
966 w->widgets[WIDX_PEOPLE_STARTING_POSITION].type = WindowWidgetType::FlatBtn;
967 w->widgets[WIDX_MAP_SIZE_SPINNER].type = WindowWidgetType::Spinner;
968 w->widgets[WIDX_MAP_SIZE_SPINNER_UP].type = WindowWidgetType::Button;
969 w->widgets[WIDX_MAP_SIZE_SPINNER_DOWN].type = WindowWidgetType::Button;
970
971 // Only show this in the scenario editor, even when in sandbox mode.
972 if (gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR)
973 w->widgets[WIDX_MAP_GENERATOR].type = WindowWidgetType::Button;
974
975 auto ft = Formatter::Common();
976 ft.Increment(2);
977 ft.Add<uint16_t>(gMapSize - 2);
978 }
979
window_map_inputsize_land(rct_window * w)980 static void window_map_inputsize_land(rct_window* w)
981 {
982 Formatter ft;
983 ft.Add<int16_t>(MINIMUM_TOOL_SIZE);
984 ft.Add<int16_t>(MAXIMUM_TOOL_SIZE);
985 window_text_input_open(w, WIDX_LAND_TOOL, STR_SELECTION_SIZE, STR_ENTER_SELECTION_SIZE, ft, STR_NONE, STR_NONE, 3);
986 }
987
window_map_inputsize_map(rct_window * w)988 static void window_map_inputsize_map(rct_window* w)
989 {
990 Formatter ft;
991 ft.Add<int16_t>(MINIMUM_MAP_SIZE_PRACTICAL);
992 ft.Add<int16_t>(MAXIMUM_MAP_SIZE_PRACTICAL);
993 window_text_input_open(w, WIDX_MAP_SIZE_SPINNER, STR_MAP_SIZE_2, STR_ENTER_MAP_SIZE, ft, STR_NONE, STR_NONE, 4);
994 }
995
window_map_draw_tab_images(rct_window * w,rct_drawpixelinfo * dpi)996 static void window_map_draw_tab_images(rct_window* w, rct_drawpixelinfo* dpi)
997 {
998 uint32_t image;
999
1000 // Guest tab image (animated)
1001 image = SPR_TAB_GUESTS_0;
1002 if (w->selected_tab == PAGE_PEEPS)
1003 image += w->list_information_type / 4;
1004
1005 gfx_draw_sprite(
1006 dpi, ImageId(image),
1007 w->windowPos + ScreenCoordsXY{ w->widgets[WIDX_PEOPLE_TAB].left, w->widgets[WIDX_PEOPLE_TAB].top });
1008
1009 // Ride/stall tab image (animated)
1010 image = SPR_TAB_RIDE_0;
1011 if (w->selected_tab == PAGE_RIDES)
1012 image += w->list_information_type / 4;
1013
1014 gfx_draw_sprite(
1015 dpi, ImageId(image), w->windowPos + ScreenCoordsXY{ w->widgets[WIDX_RIDES_TAB].left, w->widgets[WIDX_RIDES_TAB].top });
1016 }
1017
1018 /**
1019 *
1020 * part of window_map_paint_peep_overlay and window_map_paint_train_overlay
1021 */
window_map_transform_to_map_coords(CoordsXY c)1022 static MapCoordsXY window_map_transform_to_map_coords(CoordsXY c)
1023 {
1024 int32_t x = c.x, y = c.y;
1025
1026 switch (get_current_rotation())
1027 {
1028 case 3:
1029 std::swap(x, y);
1030 x = MAXIMUM_MAP_SIZE_BIG - 1 - x;
1031 break;
1032 case 2:
1033 x = MAXIMUM_MAP_SIZE_BIG - 1 - x;
1034 y = MAXIMUM_MAP_SIZE_BIG - 1 - y;
1035 break;
1036 case 1:
1037 std::swap(x, y);
1038 y = MAXIMUM_MAP_SIZE_BIG - 1 - y;
1039 break;
1040 case 0:
1041 break;
1042 }
1043 x /= 32;
1044 y /= 32;
1045
1046 return { -x + y + MAXIMUM_MAP_SIZE_TECHNICAL - 8, x + y - 8 };
1047 }
1048
DrawMapPeepPixel(Peep * peep,const uint8_t flashColour,rct_drawpixelinfo * dpi)1049 static void DrawMapPeepPixel(Peep* peep, const uint8_t flashColour, rct_drawpixelinfo* dpi)
1050 {
1051 if (peep->x == LOCATION_NULL)
1052 return;
1053
1054 MapCoordsXY c = window_map_transform_to_map_coords({ peep->x, peep->y });
1055 auto leftTop = ScreenCoordsXY{ c.x, c.y };
1056 auto rightBottom = leftTop;
1057 uint8_t colour = DefaultPeepMapColour;
1058 if (sprite_get_flashing(peep))
1059 {
1060 colour = flashColour;
1061 // If flashing then map peep pixel size is increased (by moving left top downwards)
1062 if (flashColour != DefaultPeepMapColour)
1063 {
1064 leftTop.x--;
1065 }
1066 }
1067
1068 gfx_fill_rect(dpi, { leftTop, rightBottom }, colour);
1069 }
1070
MapGetGuestFlashColour()1071 static uint8_t MapGetGuestFlashColour()
1072 {
1073 uint8_t colour = DefaultPeepMapColour;
1074 if ((gWindowMapFlashingFlags & MapFlashingFlags::FlashGuests) != 0)
1075 {
1076 colour = GuestMapColour;
1077 if ((gWindowMapFlashingFlags & MapFlashingFlags::SwitchColour) == 0)
1078 colour = GuestMapColourAlternate;
1079 }
1080 return colour;
1081 }
1082
MapGetStaffFlashColour()1083 static uint8_t MapGetStaffFlashColour()
1084 {
1085 uint8_t colour = DefaultPeepMapColour;
1086 if ((gWindowMapFlashingFlags & MapFlashingFlags::FlashStaff) != 0)
1087 {
1088 colour = StaffMapColour;
1089 if ((gWindowMapFlashingFlags & MapFlashingFlags::SwitchColour) == 0)
1090 colour = StaffMapColourAlternate;
1091 }
1092 return colour;
1093 }
1094
1095 /**
1096 *
1097 * rct2: 0x0068DADA
1098 */
window_map_paint_peep_overlay(rct_drawpixelinfo * dpi)1099 static void window_map_paint_peep_overlay(rct_drawpixelinfo* dpi)
1100 {
1101 auto flashColour = MapGetGuestFlashColour();
1102 for (auto guest : EntityList<Guest>())
1103 {
1104 DrawMapPeepPixel(guest, flashColour, dpi);
1105 }
1106 flashColour = MapGetStaffFlashColour();
1107 for (auto staff : EntityList<Staff>())
1108 {
1109 DrawMapPeepPixel(staff, flashColour, dpi);
1110 }
1111 }
1112
1113 /**
1114 *
1115 * rct2: 0x0068DBC1
1116 */
window_map_paint_train_overlay(rct_drawpixelinfo * dpi)1117 static void window_map_paint_train_overlay(rct_drawpixelinfo* dpi)
1118 {
1119 for (auto train : TrainManager::View())
1120 {
1121 for (Vehicle* vehicle = train; vehicle != nullptr; vehicle = GetEntity<Vehicle>(vehicle->next_vehicle_on_train))
1122 {
1123 if (vehicle->x == LOCATION_NULL)
1124 continue;
1125
1126 MapCoordsXY c = window_map_transform_to_map_coords({ vehicle->x, vehicle->y });
1127
1128 gfx_fill_rect(dpi, { { c.x, c.y }, { c.x, c.y } }, PALETTE_INDEX_171);
1129 }
1130 }
1131 }
1132
1133 /**
1134 * The call to gfx_fill_rect was originally wrapped in sub_68DABD which made sure that arguments were ordered correctly,
1135 * but it doesn't look like it's ever necessary here so the call was removed.
1136 *
1137 * rct2: 0x0068D8CE
1138 */
window_map_paint_hud_rectangle(rct_drawpixelinfo * dpi)1139 static void window_map_paint_hud_rectangle(rct_drawpixelinfo* dpi)
1140 {
1141 rct_window* main_window = window_get_main();
1142 if (main_window == nullptr)
1143 return;
1144
1145 rct_viewport* viewport = main_window->viewport;
1146 if (viewport == nullptr)
1147 return;
1148
1149 auto offset = MiniMapOffsets[get_current_rotation()];
1150 auto leftTop = ScreenCoordsXY{ (viewport->viewPos.x >> 5) + offset.x, (viewport->viewPos.y >> 4) + offset.y };
1151 auto rightBottom = ScreenCoordsXY{ ((viewport->viewPos.x + viewport->view_width) >> 5) + offset.x,
1152 ((viewport->viewPos.y + viewport->view_height) >> 4) + offset.y };
1153 auto rightTop = ScreenCoordsXY{ rightBottom.x, leftTop.y };
1154 auto leftBottom = ScreenCoordsXY{ leftTop.x, rightBottom.y };
1155
1156 // top horizontal lines
1157 gfx_fill_rect(dpi, { leftTop, leftTop + ScreenCoordsXY{ 3, 0 } }, PALETTE_INDEX_56);
1158 gfx_fill_rect(dpi, { rightTop - ScreenCoordsXY{ 3, 0 }, rightTop }, PALETTE_INDEX_56);
1159
1160 // left vertical lines
1161 gfx_fill_rect(dpi, { leftTop, leftTop + ScreenCoordsXY{ 0, 3 } }, PALETTE_INDEX_56);
1162 gfx_fill_rect(dpi, { leftBottom - ScreenCoordsXY{ 0, 3 }, leftBottom }, PALETTE_INDEX_56);
1163
1164 // bottom horizontal lines
1165 gfx_fill_rect(dpi, { leftBottom, leftBottom + ScreenCoordsXY{ 3, 0 } }, PALETTE_INDEX_56);
1166 gfx_fill_rect(dpi, { rightBottom - ScreenCoordsXY{ 3, 0 }, rightBottom }, PALETTE_INDEX_56);
1167
1168 // right vertical lines
1169 gfx_fill_rect(dpi, { rightTop, rightTop + ScreenCoordsXY{ 0, 3 } }, PALETTE_INDEX_56);
1170 gfx_fill_rect(dpi, { rightBottom - ScreenCoordsXY{ 0, 3 }, rightBottom }, PALETTE_INDEX_56);
1171 }
1172
1173 /**
1174 *
1175 * rct2: 0x0068D24E
1176 */
window_map_set_land_rights_tool_update(const ScreenCoordsXY & screenCoords)1177 static void window_map_set_land_rights_tool_update(const ScreenCoordsXY& screenCoords)
1178 {
1179 rct_viewport* viewport;
1180
1181 map_invalidate_selection_rect();
1182 gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE;
1183 auto mapCoords = screen_get_map_xy(screenCoords, &viewport);
1184 if (!mapCoords.has_value())
1185 return;
1186
1187 gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE;
1188 gMapSelectType = MAP_SELECT_TYPE_FULL;
1189
1190 int32_t landRightsToolSize = _landRightsToolSize;
1191 if (landRightsToolSize == 0)
1192 landRightsToolSize = 1;
1193
1194 int32_t size = (landRightsToolSize * 32) - 32;
1195 int32_t radius = (landRightsToolSize * 16) - 16;
1196 mapCoords->x = (mapCoords->x - radius) & 0xFFE0;
1197 mapCoords->y = (mapCoords->y - radius) & 0xFFE0;
1198 gMapSelectPositionA = *mapCoords;
1199 gMapSelectPositionB.x = mapCoords->x + size;
1200 gMapSelectPositionB.y = mapCoords->y + size;
1201 map_invalidate_selection_rect();
1202 }
1203
1204 /**
1205 *
1206 * rct2: 0x00666EEF
1207 */
place_park_entrance_get_map_position(const ScreenCoordsXY & screenCoords)1208 static CoordsXYZD place_park_entrance_get_map_position(const ScreenCoordsXY& screenCoords)
1209 {
1210 CoordsXYZD parkEntranceMapPosition{ 0, 0, 0, INVALID_DIRECTION };
1211 const CoordsXY mapCoords = ViewportInteractionGetTileStartAtCursor(screenCoords);
1212 parkEntranceMapPosition = { mapCoords.x, mapCoords.y, 0, INVALID_DIRECTION };
1213 if (parkEntranceMapPosition.IsNull())
1214 return parkEntranceMapPosition;
1215
1216 auto surfaceElement = map_get_surface_element_at(mapCoords);
1217 if (surfaceElement == nullptr)
1218 {
1219 parkEntranceMapPosition.SetNull();
1220 return parkEntranceMapPosition;
1221 }
1222
1223 parkEntranceMapPosition.z = surfaceElement->GetWaterHeight();
1224 if (parkEntranceMapPosition.z == 0)
1225 {
1226 parkEntranceMapPosition.z = surfaceElement->GetBaseZ();
1227 if ((surfaceElement->GetSlope() & TILE_ELEMENT_SLOPE_ALL_CORNERS_UP) != 0)
1228 {
1229 parkEntranceMapPosition.z += 16;
1230 if (surfaceElement->GetSlope() & TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT)
1231 {
1232 parkEntranceMapPosition.z += 16;
1233 }
1234 }
1235 }
1236 parkEntranceMapPosition.direction = (gWindowSceneryRotation - get_current_rotation()) & 3;
1237 return parkEntranceMapPosition;
1238 }
1239
1240 /**
1241 *
1242 * rct2: 0x00666FD0
1243 */
window_map_place_park_entrance_tool_update(const ScreenCoordsXY & screenCoords)1244 static void window_map_place_park_entrance_tool_update(const ScreenCoordsXY& screenCoords)
1245 {
1246 int32_t sideDirection;
1247
1248 map_invalidate_selection_rect();
1249 map_invalidate_map_selection_tiles();
1250 gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE;
1251 gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW;
1252 gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_CONSTRUCT;
1253 CoordsXYZD parkEntrancePosition = place_park_entrance_get_map_position(screenCoords);
1254 if (parkEntrancePosition.IsNull())
1255 {
1256 park_entrance_remove_ghost();
1257 return;
1258 }
1259
1260 sideDirection = (parkEntrancePosition.direction + 1) & 3;
1261 gMapSelectionTiles.clear();
1262 gMapSelectionTiles.push_back({ parkEntrancePosition.x, parkEntrancePosition.y });
1263 gMapSelectionTiles.push_back({ parkEntrancePosition.x + CoordsDirectionDelta[sideDirection].x,
1264 parkEntrancePosition.y + CoordsDirectionDelta[sideDirection].y });
1265 gMapSelectionTiles.push_back({ parkEntrancePosition.x - CoordsDirectionDelta[sideDirection].x,
1266 parkEntrancePosition.y - CoordsDirectionDelta[sideDirection].y });
1267
1268 gMapSelectArrowPosition = parkEntrancePosition;
1269 gMapSelectArrowDirection = parkEntrancePosition.direction;
1270
1271 gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE_CONSTRUCT | MAP_SELECT_FLAG_ENABLE_ARROW;
1272 map_invalidate_map_selection_tiles();
1273 if (gParkEntranceGhostExists && parkEntrancePosition == gParkEntranceGhostPosition)
1274 {
1275 return;
1276 }
1277
1278 park_entrance_remove_ghost();
1279 park_entrance_place_ghost(parkEntrancePosition);
1280 }
1281
1282 /**
1283 *
1284 * rct2: 0x0068D4E9
1285 */
window_map_set_peep_spawn_tool_update(const ScreenCoordsXY & screenCoords)1286 static void window_map_set_peep_spawn_tool_update(const ScreenCoordsXY& screenCoords)
1287 {
1288 int32_t mapZ, direction;
1289 TileElement* tileElement;
1290
1291 map_invalidate_selection_rect();
1292 gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE;
1293 gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW;
1294 auto mapCoords = footpath_bridge_get_info_from_pos(screenCoords, &direction, &tileElement);
1295 if (mapCoords.IsNull())
1296 return;
1297
1298 mapZ = tileElement->GetBaseZ();
1299 if (tileElement->GetType() == TILE_ELEMENT_TYPE_SURFACE)
1300 {
1301 if ((tileElement->AsSurface()->GetSlope() & TILE_ELEMENT_SLOPE_ALL_CORNERS_UP) != 0)
1302 mapZ += 16;
1303 if (tileElement->AsSurface()->GetSlope() & TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT)
1304 mapZ += 16;
1305 }
1306
1307 gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE;
1308 gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE_ARROW;
1309 gMapSelectType = MAP_SELECT_TYPE_FULL;
1310 gMapSelectPositionA = mapCoords;
1311 gMapSelectPositionB = mapCoords;
1312 gMapSelectArrowPosition = CoordsXYZ{ mapCoords, mapZ };
1313 gMapSelectArrowDirection = direction_reverse(direction);
1314 map_invalidate_selection_rect();
1315 }
1316
1317 /**
1318 *
1319 * rct2: 0x006670A4
1320 */
window_map_place_park_entrance_tool_down(const ScreenCoordsXY & screenCoords)1321 static void window_map_place_park_entrance_tool_down(const ScreenCoordsXY& screenCoords)
1322 {
1323 park_entrance_remove_ghost();
1324
1325 CoordsXYZD parkEntrancePosition = place_park_entrance_get_map_position(screenCoords);
1326 if (!parkEntrancePosition.IsNull())
1327 {
1328 auto gameAction = PlaceParkEntranceAction(parkEntrancePosition, gFootpathSelectedId);
1329 auto result = GameActions::Execute(&gameAction);
1330 if (result->Error == GameActions::Status::Ok)
1331 {
1332 OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::PlaceItem, result->Position);
1333 }
1334 }
1335 }
1336
1337 /**
1338 *
1339 * rct2: 0x0068D573
1340 */
window_map_set_peep_spawn_tool_down(const ScreenCoordsXY & screenCoords)1341 static void window_map_set_peep_spawn_tool_down(const ScreenCoordsXY& screenCoords)
1342 {
1343 TileElement* tileElement;
1344 int32_t mapZ, direction;
1345
1346 // Verify footpath exists at location, and retrieve coordinates
1347 auto mapCoords = footpath_get_coordinates_from_pos(screenCoords, &direction, &tileElement);
1348 if (mapCoords.IsNull())
1349 return;
1350
1351 mapZ = tileElement->GetBaseZ();
1352
1353 auto gameAction = PlacePeepSpawnAction({ mapCoords, mapZ, static_cast<Direction>(direction) });
1354 auto result = GameActions::Execute(&gameAction);
1355 if (result->Error == GameActions::Status::Ok)
1356 {
1357 OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::PlaceItem, result->Position);
1358 }
1359 }
1360
1361 /**
1362 *
1363 * rct2: 0x0068D641
1364 */
map_window_increase_map_size()1365 static void map_window_increase_map_size()
1366 {
1367 auto increaseMapSizeAction = ChangeMapSizeAction(gMapSize + 1);
1368 GameActions::Execute(&increaseMapSizeAction);
1369 }
1370
1371 /**
1372 *
1373 * rct2: 0x0068D6B4
1374 */
map_window_decrease_map_size()1375 static void map_window_decrease_map_size()
1376 {
1377 auto decreaseMapSizeAction = ChangeMapSizeAction(gMapSize - 1);
1378 GameActions::Execute(&decreaseMapSizeAction);
1379 }
1380
1381 static constexpr const uint16_t WaterColour = MapColour(PALETTE_INDEX_195);
1382 static constexpr const uint16_t TerrainColour[] = {
1383 MapColour(PALETTE_INDEX_73), // TERRAIN_GRASS
1384 MapColour(PALETTE_INDEX_40), // TERRAIN_SAND
1385 MapColour(PALETTE_INDEX_108), // TERRAIN_DIRT
1386 MapColour(PALETTE_INDEX_12), // TERRAIN_ROCK
1387 MapColour(PALETTE_INDEX_62), // TERRAIN_MARTIAN
1388 MapColour2(PALETTE_INDEX_10, PALETTE_INDEX_16), // TERRAIN_CHECKERBOARD
1389 MapColour2(PALETTE_INDEX_73, PALETTE_INDEX_108), // TERRAIN_GRASS_CLUMPS
1390 MapColour(PALETTE_INDEX_141), // TERRAIN_ICE
1391 MapColour2(PALETTE_INDEX_172, PALETTE_INDEX_10), // TERRAIN_GRID_RED
1392 MapColour2(PALETTE_INDEX_54, PALETTE_INDEX_10), // TERRAIN_GRID_YELLOW
1393 MapColour2(PALETTE_INDEX_162, PALETTE_INDEX_10), // TERRAIN_GRID_BLUE
1394 MapColour2(PALETTE_INDEX_102, PALETTE_INDEX_10), // TERRAIN_GRID_GREEN
1395 MapColour(PALETTE_INDEX_111), // TERRAIN_SAND_DARK
1396 MapColour(PALETTE_INDEX_222), // TERRAIN_SAND_LIGHT
1397 };
1398
1399 static constexpr const uint16_t ElementTypeMaskColour[] = {
1400 0xFFFF, // TILE_ELEMENT_TYPE_SURFACE
1401 0x0000, // TILE_ELEMENT_TYPE_PATH
1402 0x00FF, // TILE_ELEMENT_TYPE_TRACK
1403 0xFF00, // TILE_ELEMENT_TYPE_SMALL_SCENERY
1404 0x0000, // TILE_ELEMENT_TYPE_ENTRANCE
1405 0xFFFF, // TILE_ELEMENT_TYPE_WALL
1406 0x0000, // TILE_ELEMENT_TYPE_LARGE_SCENERY
1407 0xFFFF, // TILE_ELEMENT_TYPE_BANNER
1408 0x0000, // TILE_ELEMENT_TYPE_CORRUPT
1409 };
1410
1411 static constexpr const uint16_t ElementTypeAddColour[] = {
1412 MapColour(PALETTE_INDEX_0), // TILE_ELEMENT_TYPE_SURFACE
1413 MapColour(PALETTE_INDEX_17), // TILE_ELEMENT_TYPE_PATH
1414 MapColour2(PALETTE_INDEX_183, PALETTE_INDEX_0), // TILE_ELEMENT_TYPE_TRACK
1415 MapColour2(PALETTE_INDEX_0, PALETTE_INDEX_99), // TILE_ELEMENT_TYPE_SMALL_SCENERY
1416 MapColour(PALETTE_INDEX_186), // TILE_ELEMENT_TYPE_ENTRANCE
1417 MapColour(PALETTE_INDEX_0), // TILE_ELEMENT_TYPE_WALL
1418 MapColour(PALETTE_INDEX_99), // TILE_ELEMENT_TYPE_LARGE_SCENERY
1419 MapColour(PALETTE_INDEX_0), // TILE_ELEMENT_TYPE_BANNER
1420 MapColour(PALETTE_INDEX_68), // TILE_ELEMENT_TYPE_CORRUPT
1421 };
1422
map_window_get_pixel_colour_peep(const CoordsXY & c)1423 static uint16_t map_window_get_pixel_colour_peep(const CoordsXY& c)
1424 {
1425 auto* surfaceElement = map_get_surface_element_at(c);
1426 if (surfaceElement == nullptr)
1427 return 0;
1428 uint16_t colour = TerrainColour[surfaceElement->GetSurfaceStyle()];
1429 if (surfaceElement->GetWaterHeight() > 0)
1430 colour = WaterColour;
1431
1432 if (!(surfaceElement->GetOwnership() & OWNERSHIP_OWNED))
1433 colour = MapColourUnowned(colour);
1434
1435 const int32_t maxSupportedTileElementType = static_cast<int32_t>(std::size(ElementTypeAddColour));
1436 auto tileElement = reinterpret_cast<TileElement*>(surfaceElement);
1437 while (!(tileElement++)->IsLastForTile())
1438 {
1439 if (tileElement->IsGhost())
1440 {
1441 colour = MapColour(PALETTE_INDEX_21);
1442 break;
1443 }
1444
1445 int32_t tileElementType = tileElement->GetType() >> 2;
1446 if (tileElementType >= maxSupportedTileElementType)
1447 {
1448 tileElementType = TILE_ELEMENT_TYPE_CORRUPT >> 2;
1449 }
1450 colour &= ElementTypeMaskColour[tileElementType];
1451 colour |= ElementTypeAddColour[tileElementType];
1452 }
1453
1454 return colour;
1455 }
1456
map_window_get_pixel_colour_ride(const CoordsXY & c)1457 static uint16_t map_window_get_pixel_colour_ride(const CoordsXY& c)
1458 {
1459 Ride* ride;
1460 uint16_t colourA = 0; // highlight colour
1461 uint16_t colourB = MapColour(PALETTE_INDEX_13); // surface colour (dark grey)
1462
1463 // as an improvement we could use first_element to show underground stuff?
1464 TileElement* tileElement = reinterpret_cast<TileElement*>(map_get_surface_element_at(c));
1465 do
1466 {
1467 if (tileElement == nullptr)
1468 break;
1469
1470 if (tileElement->IsGhost())
1471 {
1472 colourA = MapColour(PALETTE_INDEX_21);
1473 break;
1474 }
1475
1476 switch (tileElement->GetType())
1477 {
1478 case TILE_ELEMENT_TYPE_SURFACE:
1479 if (tileElement->AsSurface()->GetWaterHeight() > 0)
1480 // Why is this a different water colour as above (195)?
1481 colourB = MapColour(PALETTE_INDEX_194);
1482 if (!(tileElement->AsSurface()->GetOwnership() & OWNERSHIP_OWNED))
1483 colourB = MapColourUnowned(colourB);
1484 break;
1485 case TILE_ELEMENT_TYPE_PATH:
1486 colourA = MapColour(PALETTE_INDEX_14); // lighter grey
1487 break;
1488 case TILE_ELEMENT_TYPE_ENTRANCE:
1489 if (tileElement->AsEntrance()->GetEntranceType() == ENTRANCE_TYPE_PARK_ENTRANCE)
1490 break;
1491 ride = get_ride(tileElement->AsEntrance()->GetRideIndex());
1492 if (ride != nullptr)
1493 {
1494 const auto& colourKey = ride->GetRideTypeDescriptor().ColourKey;
1495 colourA = RideKeyColours[static_cast<size_t>(colourKey)];
1496 }
1497 break;
1498 case TILE_ELEMENT_TYPE_TRACK:
1499 ride = get_ride(tileElement->AsTrack()->GetRideIndex());
1500 if (ride != nullptr)
1501 {
1502 const auto& colourKey = ride->GetRideTypeDescriptor().ColourKey;
1503 colourA = RideKeyColours[static_cast<size_t>(colourKey)];
1504 }
1505
1506 break;
1507 }
1508 } while (!(tileElement++)->IsLastForTile());
1509
1510 if (colourA != 0)
1511 return colourA;
1512
1513 return colourB;
1514 }
1515
map_window_set_pixels(rct_window * w)1516 static void map_window_set_pixels(rct_window* w)
1517 {
1518 uint16_t colour = 0;
1519 int32_t x = 0, y = 0, dx = 0, dy = 0;
1520
1521 int32_t pos = (_currentLine * (MAP_WINDOW_MAP_SIZE - 1)) + MAXIMUM_MAP_SIZE_TECHNICAL - 1;
1522 auto destinationPosition = ScreenCoordsXY{ pos % MAP_WINDOW_MAP_SIZE, pos / MAP_WINDOW_MAP_SIZE };
1523 auto destination = _mapImageData.data() + (destinationPosition.y * MAP_WINDOW_MAP_SIZE) + destinationPosition.x;
1524 switch (get_current_rotation())
1525 {
1526 case 0:
1527 x = _currentLine * COORDS_XY_STEP;
1528 y = 0;
1529 dx = 0;
1530 dy = COORDS_XY_STEP;
1531 break;
1532 case 1:
1533 x = MAXIMUM_TILE_START_XY;
1534 y = _currentLine * COORDS_XY_STEP;
1535 dx = -COORDS_XY_STEP;
1536 dy = 0;
1537 break;
1538 case 2:
1539 x = MAXIMUM_MAP_SIZE_BIG - ((_currentLine + 1) * COORDS_XY_STEP);
1540 y = MAXIMUM_TILE_START_XY;
1541 dx = 0;
1542 dy = -COORDS_XY_STEP;
1543 break;
1544 case 3:
1545 x = 0;
1546 y = MAXIMUM_MAP_SIZE_BIG - ((_currentLine + 1) * COORDS_XY_STEP);
1547 dx = COORDS_XY_STEP;
1548 dy = 0;
1549 break;
1550 }
1551
1552 for (int32_t i = 0; i < MAXIMUM_MAP_SIZE_TECHNICAL; i++)
1553 {
1554 if (!map_is_edge({ x, y }))
1555 {
1556 switch (w->selected_tab)
1557 {
1558 case PAGE_PEEPS:
1559 colour = map_window_get_pixel_colour_peep({ x, y });
1560 break;
1561 case PAGE_RIDES:
1562 colour = map_window_get_pixel_colour_ride({ x, y });
1563 break;
1564 }
1565 destination[0] = (colour >> 8) & 0xFF;
1566 destination[1] = colour;
1567 }
1568 x += dx;
1569 y += dy;
1570
1571 destinationPosition.x++;
1572 destinationPosition.y++;
1573 destination = _mapImageData.data() + (destinationPosition.y * MAP_WINDOW_MAP_SIZE) + destinationPosition.x;
1574 }
1575 _currentLine++;
1576 if (_currentLine >= MAXIMUM_MAP_SIZE_TECHNICAL)
1577 _currentLine = 0;
1578 }
1579
map_window_screen_to_map(ScreenCoordsXY screenCoords)1580 static CoordsXY map_window_screen_to_map(ScreenCoordsXY screenCoords)
1581 {
1582 screenCoords.x = ((screenCoords.x + 8) - MAXIMUM_MAP_SIZE_TECHNICAL) / 2;
1583 screenCoords.y = ((screenCoords.y + 8)) / 2;
1584 auto location = TileCoordsXY(screenCoords.y - screenCoords.x, screenCoords.x + screenCoords.y).ToCoordsXY();
1585
1586 switch (get_current_rotation())
1587 {
1588 case 0:
1589 return location;
1590 case 1:
1591 return { MAXIMUM_MAP_SIZE_BIG - 1 - location.y, location.x };
1592 case 2:
1593 return { MAXIMUM_MAP_SIZE_BIG - 1 - location.x, MAXIMUM_MAP_SIZE_BIG - 1 - location.y };
1594 case 3:
1595 return { location.y, MAXIMUM_MAP_SIZE_BIG - 1 - location.x };
1596 }
1597
1598 return { 0, 0 }; // unreachable
1599 }
1600