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