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 "../interface/Theme.h"
11 
12 #include <openrct2-ui/interface/Dropdown.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/Context.h>
17 #include <openrct2/Game.h>
18 #include <openrct2/Input.h>
19 #include <openrct2/actions/PeepPickupAction.h>
20 #include <openrct2/actions/StaffSetCostumeAction.h>
21 #include <openrct2/actions/StaffSetOrdersAction.h>
22 #include <openrct2/actions/StaffSetPatrolAreaAction.h>
23 #include <openrct2/config/Config.h>
24 #include <openrct2/localisation/Localisation.h>
25 #include <openrct2/management/Finance.h>
26 #include <openrct2/network/network.h>
27 #include <openrct2/peep/Staff.h>
28 #include <openrct2/sprites.h>
29 #include <openrct2/windows/Intent.h>
30 #include <openrct2/world/Entity.h>
31 #include <openrct2/world/Footpath.h>
32 #include <openrct2/world/Park.h>
33 
34 static constexpr const rct_string_id WINDOW_TITLE = STR_STRINGID;
35 static constexpr const int32_t WW = 190;
36 static constexpr const int32_t WH = 180;
37 
38 enum WINDOW_STAFF_PAGE
39 {
40     WINDOW_STAFF_OVERVIEW,
41     WINDOW_STAFF_OPTIONS,
42     WINDOW_STAFF_STATISTICS,
43 };
44 
45 enum WINDOW_STAFF_WIDGET_IDX
46 {
47     WIDX_BACKGROUND,
48     WIDX_TITLE,
49     WIDX_CLOSE,
50     WIDX_RESIZE,
51     WIDX_TAB_1,
52     WIDX_TAB_2,
53     WIDX_TAB_3,
54 
55     WIDX_VIEWPORT = 7,
56     WIDX_BTM_LABEL,
57     WIDX_PICKUP,
58     WIDX_PATROL,
59     WIDX_RENAME,
60     WIDX_LOCATE,
61     WIDX_FIRE,
62 
63     WIDX_CHECKBOX_1 = 7,
64     WIDX_CHECKBOX_2,
65     WIDX_CHECKBOX_3,
66     WIDX_CHECKBOX_4,
67     WIDX_COSTUME_BOX,
68     WIDX_COSTUME_BTN,
69 };
70 
71 validate_global_widx(WC_PEEP, WIDX_PATROL);
72 validate_global_widx(WC_STAFF, WIDX_PICKUP);
73 
74 // clang-format off
75 #define MAIN_STAFF_WIDGETS \
76     WINDOW_SHIM(WINDOW_TITLE, WW, WH), \
77     MakeWidget({ 0, 43}, {190, 137}, WindowWidgetType::Resize, WindowColour::Secondary), /* Resize */ \
78     MakeTab   ({ 3, 17}, STR_STAFF_OVERVIEW_TIP                         ), /* Tab 1 */ \
79     MakeTab   ({34, 17}, STR_STAFF_OPTIONS_TIP                          ), /* Tab 2 */ \
80     MakeTab   ({65, 17}, STR_STAFF_STATS_TIP                            )  /* Tab 3 */
81 
82 static rct_widget window_staff_overview_widgets[] = {
83     MAIN_STAFF_WIDGETS,
84     MakeWidget     ({      3,      47}, {162, 120}, WindowWidgetType::Viewport,      WindowColour::Secondary                                        ), // Viewport
85     MakeWidget     ({      3, WH - 13}, {162,  11}, WindowWidgetType::LabelCentred, WindowColour::Secondary                                        ), // Label at bottom of viewport
86     MakeWidget     ({WW - 25,      45}, { 24,  24}, WindowWidgetType::FlatBtn,       WindowColour::Secondary, SPR_PICKUP_BTN, STR_PICKUP_TIP        ), // Pickup Button
87     MakeWidget     ({WW - 25,      69}, { 24,  24}, WindowWidgetType::FlatBtn,       WindowColour::Secondary, SPR_PATROL_BTN, STR_SET_PATROL_TIP    ), // Patrol Button
88     MakeWidget     ({WW - 25,      93}, { 24,  24}, WindowWidgetType::FlatBtn,       WindowColour::Secondary, SPR_RENAME,     STR_NAME_STAFF_TIP    ), // Rename Button
89     MakeWidget     ({WW - 25,     117}, { 24,  24}, WindowWidgetType::FlatBtn,       WindowColour::Secondary, SPR_LOCATE,     STR_LOCATE_SUBJECT_TIP), // Locate Button
90     MakeWidget     ({WW - 25,     141}, { 24,  24}, WindowWidgetType::FlatBtn,       WindowColour::Secondary, SPR_DEMOLISH,   STR_FIRE_STAFF_TIP    ), // Fire Button
91     WIDGETS_END,
92 };
93 
94 //0x9AF910
95 static rct_widget window_staff_options_widgets[] = {
96     MAIN_STAFF_WIDGETS,
97     MakeWidget     ({      5,  50}, {180,  12}, WindowWidgetType::Checkbox, WindowColour::Secondary                                            ), // Checkbox 1
98     MakeWidget     ({      5,  67}, {180,  12}, WindowWidgetType::Checkbox, WindowColour::Secondary                                            ), // Checkbox 2
99     MakeWidget     ({      5,  84}, {180,  12}, WindowWidgetType::Checkbox, WindowColour::Secondary                                            ), // Checkbox 3
100     MakeWidget     ({      5, 101}, {180,  12}, WindowWidgetType::Checkbox, WindowColour::Secondary                                            ), // Checkbox 4
101     MakeWidget     ({      5,  50}, {180,  12}, WindowWidgetType::DropdownMenu, WindowColour::Secondary                                            ), // Costume Dropdown
102     MakeWidget     ({WW - 17,  51}, { 11,  10}, WindowWidgetType::Button,   WindowColour::Secondary, STR_DROPDOWN_GLYPH, STR_SELECT_COSTUME_TIP), // Costume Dropdown Button
103     WIDGETS_END,
104 };
105 // clang-format on
106 
107 // 0x9AF9F4
108 static rct_widget window_staff_stats_widgets[] = {
109     MAIN_STAFF_WIDGETS,
110     WIDGETS_END,
111 };
112 
113 static rct_widget* window_staff_page_widgets[] = {
114     window_staff_overview_widgets,
115     window_staff_options_widgets,
116     window_staff_stats_widgets,
117 };
118 
119 static void window_staff_set_page(rct_window* w, int32_t page);
120 static void window_staff_disable_widgets(rct_window* w);
121 static void window_staff_unknown_05(rct_window* w);
122 static void window_staff_viewport_init(rct_window* w);
123 
124 static void window_staff_overview_close(rct_window* w);
125 static void window_staff_overview_mouseup(rct_window* w, rct_widgetindex widgetIndex);
126 static void window_staff_overview_resize(rct_window* w);
127 static void window_staff_overview_mousedown(rct_window* w, rct_widgetindex widgetIndex, rct_widget* widget);
128 static void window_staff_overview_dropdown(rct_window* w, rct_widgetindex widgetIndex, int32_t dropdownIndex);
129 static void window_staff_overview_update(rct_window* w);
130 static void window_staff_overview_invalidate(rct_window* w);
131 static void window_staff_overview_paint(rct_window* w, rct_drawpixelinfo* dpi);
132 static void window_staff_overview_tab_paint(rct_window* w, rct_drawpixelinfo* dpi);
133 static void window_staff_overview_tool_update(rct_window* w, rct_widgetindex widgetIndex, const ScreenCoordsXY& screenCoords);
134 static void window_staff_overview_tool_down(rct_window* w, rct_widgetindex widgetIndex, const ScreenCoordsXY& screenCoords);
135 static void window_staff_overview_tool_drag(rct_window* w, rct_widgetindex widgetIndex, const ScreenCoordsXY& screenCoords);
136 static void window_staff_overview_tool_up(rct_window* w, rct_widgetindex widgetIndex, const ScreenCoordsXY& screenCoords);
137 static void window_staff_overview_tool_abort(rct_window* w, rct_widgetindex widgetIndex);
138 static void window_staff_overview_text_input(rct_window* w, rct_widgetindex widgetIndex, char* text);
139 static void window_staff_overview_viewport_rotate(rct_window* w);
140 static void window_staff_follow(rct_window* w);
141 static void window_staff_show_locate_dropdown(rct_window* w, rct_widget* widget);
142 
143 static void window_staff_options_mouseup(rct_window* w, rct_widgetindex widgetIndex);
144 static void window_staff_options_update(rct_window* w);
145 static void window_staff_options_invalidate(rct_window* w);
146 static void window_staff_options_paint(rct_window* w, rct_drawpixelinfo* dpi);
147 static void window_staff_options_tab_paint(rct_window* w, rct_drawpixelinfo* dpi);
148 static void window_staff_options_mousedown(rct_window* w, rct_widgetindex widgetIndex, rct_widget* widget);
149 static void window_staff_options_dropdown(rct_window* w, rct_widgetindex widgetIndex, int32_t dropdownIndex);
150 
151 static void window_staff_stats_mouseup(rct_window* w, rct_widgetindex widgetIndex);
152 static void window_staff_stats_resize(rct_window* w);
153 static void window_staff_stats_update(rct_window* w);
154 static void window_staff_stats_invalidate(rct_window* w);
155 static void window_staff_stats_paint(rct_window* w, rct_drawpixelinfo* dpi);
156 static void window_staff_stats_tab_paint(rct_window* w, rct_drawpixelinfo* dpi);
157 
158 void window_staff_set_colours();
159 
160 // 0x992AEC
__anonde333b5b0102(auto& events) 161 static rct_window_event_list window_staff_overview_events([](auto& events) {
162     events.close = &window_staff_overview_close;
163     events.mouse_up = &window_staff_overview_mouseup;
164     events.resize = &window_staff_overview_resize;
165     events.mouse_down = &window_staff_overview_mousedown;
166     events.dropdown = &window_staff_overview_dropdown;
167     events.update = &window_staff_overview_update;
168     events.tool_update = &window_staff_overview_tool_update;
169     events.tool_down = &window_staff_overview_tool_down;
170     events.tool_drag = &window_staff_overview_tool_drag;
171     events.tool_up = &window_staff_overview_tool_up;
172     events.tool_abort = &window_staff_overview_tool_abort;
173     events.text_input = &window_staff_overview_text_input;
174     events.viewport_rotate = &window_staff_overview_viewport_rotate;
175     events.invalidate = &window_staff_overview_invalidate;
176     events.paint = &window_staff_overview_paint;
177 });
178 
179 // 0x992B5C
__anonde333b5b0202(auto& events) 180 static rct_window_event_list window_staff_options_events([](auto& events) {
181     events.mouse_up = &window_staff_options_mouseup;
182     events.resize = &window_staff_stats_resize;
183     events.mouse_down = &window_staff_options_mousedown;
184     events.dropdown = &window_staff_options_dropdown;
185     events.unknown_05 = &window_staff_unknown_05;
186     events.update = &window_staff_options_update;
187     events.invalidate = &window_staff_options_invalidate;
188     events.paint = &window_staff_options_paint;
189 });
190 
191 // 0x992BCC
__anonde333b5b0302(auto& events) 192 static rct_window_event_list window_staff_stats_events([](auto& events) {
193     events.mouse_up = &window_staff_stats_mouseup;
194     events.resize = &window_staff_stats_resize;
195     events.unknown_05 = &window_staff_unknown_05;
196     events.update = &window_staff_stats_update;
197     events.invalidate = &window_staff_stats_invalidate;
198     events.paint = &window_staff_stats_paint;
199 });
200 
201 static rct_window_event_list* window_staff_page_events[] = {
202     &window_staff_overview_events,
203     &window_staff_options_events,
204     &window_staff_stats_events,
205 };
206 
207 // clang-format off
208 static constexpr const uint32_t window_staff_page_enabled_widgets[] = {
209     (1ULL << WIDX_CLOSE) |
210     (1ULL << WIDX_TAB_1) |
211     (1ULL << WIDX_TAB_2) |
212     (1ULL << WIDX_TAB_3) |
213     (1ULL << WIDX_PICKUP) |
214     (1ULL << WIDX_PATROL) |
215     (1ULL << WIDX_RENAME) |
216     (1ULL << WIDX_LOCATE) |
217     (1ULL << WIDX_FIRE),
218 
219     (1ULL << WIDX_CLOSE) |
220     (1ULL << WIDX_TAB_1) |
221     (1ULL << WIDX_TAB_2) |
222     (1ULL << WIDX_TAB_3) |
223     (1ULL << WIDX_CHECKBOX_1) |
224     (1ULL << WIDX_CHECKBOX_2) |
225     (1ULL << WIDX_CHECKBOX_3) |
226     (1ULL << WIDX_CHECKBOX_4) |
227     (1ULL << WIDX_COSTUME_BTN),
228 
229     (1ULL << WIDX_CLOSE) |
230     (1ULL << WIDX_TAB_1) |
231     (1ULL << WIDX_TAB_2) |
232     (1ULL << WIDX_TAB_3)
233 };
234 // clang-format on
235 
236 static EntertainerCostume _availableCostumes[static_cast<uint8_t>(EntertainerCostume::Count)];
237 
238 enum class PatrolAreaValue
239 {
240     UNSET = 0,
241     SET = 1,
242     NONE = -1,
243 };
244 
245 static PatrolAreaValue _staffPatrolAreaPaintValue = PatrolAreaValue::NONE;
246 
GetStaff(rct_window * w)247 static Staff* GetStaff(rct_window* w)
248 {
249     auto staff = GetEntity<Staff>(w->number);
250     if (staff == nullptr)
251     {
252         window_close(w);
253         return nullptr;
254     }
255     return staff;
256 }
257 
258 /**
259  *
260  *  rct2: 0x006BEE98
261  */
window_staff_open(Peep * peep)262 rct_window* window_staff_open(Peep* peep)
263 {
264     rct_window* w = window_bring_to_front_by_number(WC_PEEP, peep->sprite_index);
265     if (w == nullptr)
266     {
267         w = WindowCreateAutoPos(WW, WH, &window_staff_overview_events, WC_PEEP, WF_10 | WF_RESIZABLE);
268 
269         w->number = peep->sprite_index;
270         w->page = 0;
271         w->frame_no = 0;
272         w->highlighted_item = 0;
273 
274         window_staff_disable_widgets(w);
275 
276         w->min_width = WW;
277         w->min_height = WH;
278         w->max_width = 500;
279         w->max_height = 450;
280     }
281     w->page = 0;
282     w->Invalidate();
283 
284     w->widgets = window_staff_overview_widgets;
285     w->enabled_widgets = window_staff_page_enabled_widgets[0];
286     w->hold_down_widgets = 0;
287     w->event_handlers = window_staff_page_events[0];
288     w->pressed_widgets = 0;
289     window_staff_disable_widgets(w);
290     WindowInitScrollWidgets(w);
291     window_staff_viewport_init(w);
292 
293     if (peep->State == PeepState::Picked)
294         window_event_mouse_up_call(w, WIDX_CHECKBOX_3);
295 
296     return w;
297 }
298 
299 /**
300  * rct2: 0x006BED21
301  * Disable the staff pickup if not in pickup state.
302  */
window_staff_disable_widgets(rct_window * w)303 void window_staff_disable_widgets(rct_window* w)
304 {
305     const auto peep = GetStaff(w);
306     if (peep == nullptr)
307     {
308         return;
309     }
310     uint64_t disabled_widgets = 0;
311 
312     if (peep != nullptr && peep->AssignedStaffType == StaffType::Security)
313     {
314         disabled_widgets |= (1ULL << WIDX_TAB_2);
315     }
316 
317     if (w->page == WINDOW_STAFF_OVERVIEW)
318     {
319         if (peep->CanBePickedUp())
320         {
321             if (w->disabled_widgets & (1ULL << WIDX_PICKUP))
322                 w->Invalidate();
323         }
324         else
325         {
326             disabled_widgets |= (1ULL << WIDX_PICKUP);
327             if (!(w->disabled_widgets & (1ULL << WIDX_PICKUP)))
328                 w->Invalidate();
329         }
330     }
331 
332     w->disabled_widgets = disabled_widgets;
333 }
334 
335 /**
336  * Same as window_peep_overview_close.
337  *  rct2: 0x006BDFF8
338  */
window_staff_overview_close(rct_window * w)339 void window_staff_overview_close(rct_window* w)
340 {
341     if (input_test_flag(INPUT_FLAG_TOOL_ACTIVE))
342     {
343         if (w->classification == gCurrentToolWidget.window_classification && w->number == gCurrentToolWidget.window_number)
344             tool_cancel();
345     }
346 }
347 
348 /**
349  * Mostly similar to window_peep_set_page.
350  *  rct2: 0x006BE023
351  */
window_staff_set_page(rct_window * w,int32_t page)352 void window_staff_set_page(rct_window* w, int32_t page)
353 {
354     if (input_test_flag(INPUT_FLAG_TOOL_ACTIVE))
355     {
356         if (w->number == gCurrentToolWidget.window_number && w->classification == gCurrentToolWidget.window_classification)
357             tool_cancel();
358     }
359 
360     int32_t listen = 0;
361     if (page == WINDOW_STAFF_OVERVIEW && w->page == WINDOW_STAFF_OVERVIEW && w->viewport != nullptr)
362     {
363         if (!(w->viewport->flags & VIEWPORT_FLAG_SOUND_ON))
364             listen = 1;
365     }
366 
367     w->page = page;
368     w->frame_no = 0;
369 
370     w->RemoveViewport();
371 
372     w->enabled_widgets = window_staff_page_enabled_widgets[page];
373     w->hold_down_widgets = 0;
374     w->event_handlers = window_staff_page_events[page];
375     w->pressed_widgets = 0;
376     w->widgets = window_staff_page_widgets[page];
377 
378     window_staff_disable_widgets(w);
379     w->Invalidate();
380 
381     window_event_resize_call(w);
382     window_event_invalidate_call(w);
383 
384     WindowInitScrollWidgets(w);
385     w->Invalidate();
386 
387     if (listen && w->viewport != nullptr)
388         w->viewport->flags |= VIEWPORT_FLAG_SOUND_ON;
389 }
390 
391 /**
392  *
393  *  rct2: 0x006BDF55
394  */
window_staff_overview_mouseup(rct_window * w,rct_widgetindex widgetIndex)395 void window_staff_overview_mouseup(rct_window* w, rct_widgetindex widgetIndex)
396 {
397     const auto peep = GetStaff(w);
398     if (peep == nullptr)
399     {
400         return;
401     }
402 
403     switch (widgetIndex)
404     {
405         case WIDX_CLOSE:
406             window_close(w);
407             break;
408         case WIDX_TAB_1:
409         case WIDX_TAB_2:
410         case WIDX_TAB_3:
411             window_staff_set_page(w, widgetIndex - WIDX_TAB_1);
412             break;
413         case WIDX_PICKUP:
414         {
415             w->picked_peep_old_x = peep->x;
416             CoordsXYZ nullLoc{};
417             nullLoc.SetNull();
418             PeepPickupAction pickupAction{ PeepPickupType::Pickup, w->number, nullLoc, network_get_current_player_id() };
419             pickupAction.SetCallback([peepnum = w->number](const GameAction* ga, const GameActions::Result* result) {
420                 if (result->Error != GameActions::Status::Ok)
421                     return;
422                 rct_window* wind = window_find_by_number(WC_PEEP, peepnum);
423                 if (wind != nullptr)
424                 {
425                     tool_set(wind, WC_STAFF__WIDX_PICKUP, Tool::Picker);
426                 }
427             });
428             GameActions::Execute(&pickupAction);
429         }
430         break;
431         case WIDX_FIRE:
432         {
433             auto intent = Intent(WC_FIRE_PROMPT);
434             intent.putExtra(INTENT_EXTRA_PEEP, peep);
435             context_open_intent(&intent);
436             break;
437         }
438         case WIDX_RENAME:
439         {
440             auto peepName = peep->GetName();
441             window_text_input_raw_open(
442                 w, widgetIndex, STR_STAFF_TITLE_STAFF_MEMBER_NAME, STR_STAFF_PROMPT_ENTER_NAME, {}, peepName.c_str(), 32);
443             break;
444         }
445     }
446 }
447 
448 /**
449  *
450  *  rct2: 0x006BE558
451  */
window_staff_overview_resize(rct_window * w)452 void window_staff_overview_resize(rct_window* w)
453 {
454     window_staff_disable_widgets(w);
455 
456     w->min_width = WW;
457     w->max_width = 500;
458     w->min_height = WH;
459     w->max_height = 450;
460 
461     if (w->width < w->min_width)
462     {
463         w->width = w->min_width;
464         w->Invalidate();
465     }
466 
467     if (w->width > w->max_width)
468     {
469         w->Invalidate();
470         w->width = w->max_width;
471     }
472 
473     if (w->height < w->min_height)
474     {
475         w->height = w->min_height;
476         w->Invalidate();
477     }
478 
479     if (w->height > w->max_height)
480     {
481         w->Invalidate();
482         w->height = w->max_height;
483     }
484 
485     rct_viewport* viewport = w->viewport;
486     if (viewport != nullptr)
487     {
488         int32_t new_width = w->width - 30;
489         int32_t new_height = w->height - 62;
490 
491         // Update the viewport size
492         if (viewport->width != new_width || viewport->height != new_height)
493         {
494             viewport->width = new_width;
495             viewport->height = new_height;
496             viewport->view_width = new_width * viewport->zoom;
497             viewport->view_height = new_height * viewport->zoom;
498         }
499     }
500 
501     window_staff_viewport_init(w);
502 }
503 
504 /**
505  * Handle the dropdown of patrol button.
506  *  rct2: 0x006BDF98
507  */
window_staff_overview_mousedown(rct_window * w,rct_widgetindex widgetIndex,rct_widget * widget)508 void window_staff_overview_mousedown(rct_window* w, rct_widgetindex widgetIndex, rct_widget* widget)
509 {
510     switch (widgetIndex)
511     {
512         case WIDX_LOCATE:
513             window_staff_show_locate_dropdown(w, widget);
514             break;
515         case WIDX_PATROL:
516         {
517             // Dropdown names
518             gDropdownItemsFormat[0] = STR_SET_PATROL_AREA;
519             gDropdownItemsFormat[1] = STR_CLEAR_PATROL_AREA;
520 
521             auto dropdownPos = ScreenCoordsXY{ widget->left + w->windowPos.x, widget->top + w->windowPos.y };
522             int32_t extray = widget->height() + 1;
523             WindowDropdownShowText(dropdownPos, extray, w->colours[1], 0, 2);
524             gDropdownDefaultIndex = 0;
525 
526             const auto peep = GetStaff(w);
527             if (peep == nullptr)
528             {
529                 return;
530             }
531 
532             // Disable clear patrol area if no area is set.
533             if (!peep->HasPatrolArea())
534             {
535                 Dropdown::SetDisabled(1, true);
536             }
537         }
538     }
539 }
540 
541 /**
542  *
543  *  rct2: 0x006BDFA3
544  */
window_staff_overview_dropdown(rct_window * w,rct_widgetindex widgetIndex,int32_t dropdownIndex)545 void window_staff_overview_dropdown(rct_window* w, rct_widgetindex widgetIndex, int32_t dropdownIndex)
546 {
547     switch (widgetIndex)
548     {
549         case WIDX_LOCATE:
550         {
551             if (dropdownIndex == 0)
552             {
553                 w->ScrollToViewport();
554             }
555             else if (dropdownIndex == 1)
556             {
557                 window_staff_follow(w);
558             }
559             break;
560         }
561         case WIDX_PATROL:
562         {
563             // Clear patrol
564             if (dropdownIndex == 1)
565             {
566                 const auto peep = GetStaff(w);
567                 if (peep == nullptr)
568                 {
569                     return;
570                 }
571 
572                 auto staffSetPatrolAreaAction = StaffSetPatrolAreaAction(
573                     peep->sprite_index, {}, StaffSetPatrolAreaMode::ClearAll);
574                 GameActions::Execute(&staffSetPatrolAreaAction);
575             }
576             else
577             {
578                 if (!tool_set(w, widgetIndex, Tool::WalkDown))
579                 {
580                     show_gridlines();
581                     gStaffDrawPatrolAreas = w->number;
582                     gfx_invalidate_screen();
583                 }
584             }
585             break;
586         }
587     }
588 }
589 
window_staff_show_locate_dropdown(rct_window * w,rct_widget * widget)590 static void window_staff_show_locate_dropdown(rct_window* w, rct_widget* widget)
591 {
592     gDropdownItemsFormat[0] = STR_LOCATE_SUBJECT_TIP;
593     gDropdownItemsFormat[1] = STR_FOLLOW_SUBJECT_TIP;
594 
595     WindowDropdownShowText(
596         { w->windowPos.x + widget->left, w->windowPos.y + widget->top }, widget->height() + 1, w->colours[1], 0, 2);
597     gDropdownDefaultIndex = 0;
598 }
599 
window_staff_follow(rct_window * w)600 static void window_staff_follow(rct_window* w)
601 {
602     rct_window* w_main = window_get_main();
603     window_follow_sprite(w_main, w->number);
604 }
605 
606 /**
607  * Update the animation frame of the tab icon.
608  *  rct2: 0x6BE602
609  */
window_staff_overview_update(rct_window * w)610 void window_staff_overview_update(rct_window* w)
611 {
612     int32_t newAnimationFrame = w->var_496;
613     newAnimationFrame++;
614     if (newAnimationFrame >= 24)
615     {
616         newAnimationFrame = 0;
617     }
618     w->var_496 = newAnimationFrame;
619     widget_invalidate(w, WIDX_TAB_1);
620 }
621 
622 /**
623  *
624  *  rct2: 0x006BE814
625  */
window_staff_set_order(rct_window * w,int32_t order_id)626 static void window_staff_set_order(rct_window* w, int32_t order_id)
627 {
628     const auto peep = GetStaff(w);
629     if (peep == nullptr)
630     {
631         return;
632     }
633 
634     uint8_t newOrders = peep->StaffOrders ^ (1 << order_id);
635     auto staffSetOrdersAction = StaffSetOrdersAction(w->number, newOrders);
636     GameActions::Execute(&staffSetOrdersAction);
637 }
638 
639 /**
640  *
641  *  rct2: 0x006BE7DB
642  */
window_staff_options_mouseup(rct_window * w,rct_widgetindex widgetIndex)643 void window_staff_options_mouseup(rct_window* w, rct_widgetindex widgetIndex)
644 {
645     switch (widgetIndex)
646     {
647         case WIDX_CLOSE:
648             window_close(w);
649             break;
650         case WIDX_TAB_1:
651         case WIDX_TAB_2:
652         case WIDX_TAB_3:
653             window_staff_set_page(w, widgetIndex - WIDX_TAB_1);
654             break;
655         case WIDX_CHECKBOX_1:
656         case WIDX_CHECKBOX_2:
657         case WIDX_CHECKBOX_3:
658         case WIDX_CHECKBOX_4:
659             window_staff_set_order(w, widgetIndex - WIDX_CHECKBOX_1);
660             break;
661     }
662 }
663 
664 /**
665  *
666  *  rct2: 0x006BE960
667  */
window_staff_options_update(rct_window * w)668 void window_staff_options_update(rct_window* w)
669 {
670     w->frame_no++;
671     widget_invalidate(w, WIDX_TAB_2);
672 }
673 
674 /**
675  *
676  *  rct2: 0x006BEBCF
677  */
window_staff_stats_mouseup(rct_window * w,rct_widgetindex widgetIndex)678 void window_staff_stats_mouseup(rct_window* w, rct_widgetindex widgetIndex)
679 {
680     switch (widgetIndex)
681     {
682         case WIDX_CLOSE:
683             window_close(w);
684             break;
685         case WIDX_TAB_1:
686         case WIDX_TAB_2:
687         case WIDX_TAB_3:
688             window_staff_set_page(w, widgetIndex - WIDX_TAB_1);
689             break;
690     }
691 }
692 
693 /**
694  *
695  *  rct2: 0x006BEC1B, 0x006BE975
696  */
window_staff_stats_resize(rct_window * w)697 void window_staff_stats_resize(rct_window* w)
698 {
699     w->min_width = 190;
700     w->max_width = 190;
701     w->min_height = 126;
702     w->max_height = 126;
703 
704     if (w->width < w->min_width)
705     {
706         w->width = w->min_width;
707         w->Invalidate();
708     }
709 
710     if (w->width > w->max_width)
711     {
712         w->Invalidate();
713         w->width = w->max_width;
714     }
715 
716     if (w->height < w->min_height)
717     {
718         w->height = w->min_height;
719         w->Invalidate();
720     }
721 
722     if (w->height > w->max_height)
723     {
724         w->Invalidate();
725         w->height = w->max_height;
726     }
727 }
728 
729 /**
730  *
731  *  rct2: 0x006BEBEA
732  */
window_staff_stats_update(rct_window * w)733 void window_staff_stats_update(rct_window* w)
734 {
735     w->frame_no++;
736     widget_invalidate(w, WIDX_TAB_3);
737 
738     auto peep = GetStaff(w);
739     if (peep == nullptr)
740     {
741         return;
742     }
743     if (peep->WindowInvalidateFlags & PEEP_INVALIDATE_STAFF_STATS)
744     {
745         peep->WindowInvalidateFlags &= ~PEEP_INVALIDATE_STAFF_STATS;
746         w->Invalidate();
747     }
748 }
749 
750 /**
751  *
752  *  rct2: 0x6BEC80, 0x6BE9DA
753  */
window_staff_unknown_05(rct_window * w)754 void window_staff_unknown_05(rct_window* w)
755 {
756     widget_invalidate(w, WIDX_TAB_1);
757 }
758 
759 /**
760  *
761  *  rct2: 0x006BE9E9
762  */
window_staff_stats_invalidate(rct_window * w)763 void window_staff_stats_invalidate(rct_window* w)
764 {
765     ColourSchemeUpdateByClass(w, static_cast<rct_windowclass>(WC_STAFF));
766 
767     if (window_staff_page_widgets[w->page] != w->widgets)
768     {
769         w->widgets = window_staff_page_widgets[w->page];
770         WindowInitScrollWidgets(w);
771     }
772 
773     w->pressed_widgets |= 1ULL << (w->page + WIDX_TAB_1);
774 
775     const auto peep = GetStaff(w);
776     if (peep == nullptr)
777     {
778         return;
779     }
780 
781     auto ft = Formatter::Common();
782     peep->FormatNameTo(ft);
783 
784     window_staff_stats_widgets[WIDX_BACKGROUND].right = w->width - 1;
785     window_staff_stats_widgets[WIDX_BACKGROUND].bottom = w->height - 1;
786 
787     window_staff_stats_widgets[WIDX_RESIZE].right = w->width - 1;
788     window_staff_stats_widgets[WIDX_RESIZE].bottom = w->height - 1;
789 
790     window_staff_stats_widgets[WIDX_TITLE].right = w->width - 2;
791 
792     window_staff_stats_widgets[WIDX_CLOSE].left = w->width - 13;
793     window_staff_stats_widgets[WIDX_CLOSE].right = w->width - 3;
794 
795     window_align_tabs(w, WIDX_TAB_1, WIDX_TAB_3);
796 }
797 
798 /**
799  *
800  *  rct2: 0x006BE62B
801  */
window_staff_options_invalidate(rct_window * w)802 void window_staff_options_invalidate(rct_window* w)
803 {
804     ColourSchemeUpdateByClass(w, static_cast<rct_windowclass>(WC_STAFF));
805 
806     if (window_staff_page_widgets[w->page] != w->widgets)
807     {
808         w->widgets = window_staff_page_widgets[w->page];
809         WindowInitScrollWidgets(w);
810     }
811 
812     w->pressed_widgets |= 1ULL << (w->page + WIDX_TAB_1);
813 
814     const auto peep = GetStaff(w);
815     if (peep == nullptr)
816     {
817         return;
818     }
819     auto ft = Formatter::Common();
820     peep->FormatNameTo(ft);
821 
822     switch (peep->AssignedStaffType)
823     {
824         case StaffType::Entertainer:
825             window_staff_options_widgets[WIDX_CHECKBOX_1].type = WindowWidgetType::Empty;
826             window_staff_options_widgets[WIDX_CHECKBOX_2].type = WindowWidgetType::Empty;
827             window_staff_options_widgets[WIDX_CHECKBOX_3].type = WindowWidgetType::Empty;
828             window_staff_options_widgets[WIDX_CHECKBOX_4].type = WindowWidgetType::Empty;
829             window_staff_options_widgets[WIDX_COSTUME_BOX].type = WindowWidgetType::DropdownMenu;
830             window_staff_options_widgets[WIDX_COSTUME_BTN].type = WindowWidgetType::Button;
831             window_staff_options_widgets[WIDX_COSTUME_BOX].text = StaffCostumeNames[EnumValue(peep->SpriteType) - 4];
832             break;
833         case StaffType::Handyman:
834             window_staff_options_widgets[WIDX_CHECKBOX_1].type = WindowWidgetType::Checkbox;
835             window_staff_options_widgets[WIDX_CHECKBOX_1].text = STR_STAFF_OPTION_SWEEP_FOOTPATHS;
836             window_staff_options_widgets[WIDX_CHECKBOX_2].type = WindowWidgetType::Checkbox;
837             window_staff_options_widgets[WIDX_CHECKBOX_2].text = STR_STAFF_OPTION_WATER_GARDENS;
838             window_staff_options_widgets[WIDX_CHECKBOX_3].type = WindowWidgetType::Checkbox;
839             window_staff_options_widgets[WIDX_CHECKBOX_3].text = STR_STAFF_OPTION_EMPTY_LITTER;
840             window_staff_options_widgets[WIDX_CHECKBOX_4].type = WindowWidgetType::Checkbox;
841             window_staff_options_widgets[WIDX_CHECKBOX_4].text = STR_STAFF_OPTION_MOW_GRASS;
842             window_staff_options_widgets[WIDX_COSTUME_BOX].type = WindowWidgetType::Empty;
843             window_staff_options_widgets[WIDX_COSTUME_BTN].type = WindowWidgetType::Empty;
844             w->pressed_widgets &= ~(
845                 (1ULL << WIDX_CHECKBOX_1) | (1ULL << WIDX_CHECKBOX_2) | (1ULL << WIDX_CHECKBOX_3) | (1ULL << WIDX_CHECKBOX_4));
846             w->pressed_widgets |= peep->StaffOrders << WIDX_CHECKBOX_1;
847             break;
848         case StaffType::Mechanic:
849             window_staff_options_widgets[WIDX_CHECKBOX_1].type = WindowWidgetType::Checkbox;
850             window_staff_options_widgets[WIDX_CHECKBOX_1].text = STR_INSPECT_RIDES;
851             window_staff_options_widgets[WIDX_CHECKBOX_2].type = WindowWidgetType::Checkbox;
852             window_staff_options_widgets[WIDX_CHECKBOX_2].text = STR_FIX_RIDES;
853             window_staff_options_widgets[WIDX_CHECKBOX_3].type = WindowWidgetType::Empty;
854             window_staff_options_widgets[WIDX_CHECKBOX_4].type = WindowWidgetType::Empty;
855             window_staff_options_widgets[WIDX_COSTUME_BOX].type = WindowWidgetType::Empty;
856             window_staff_options_widgets[WIDX_COSTUME_BTN].type = WindowWidgetType::Empty;
857             w->pressed_widgets &= ~((1ULL << WIDX_CHECKBOX_1) | (1ULL << WIDX_CHECKBOX_2));
858             w->pressed_widgets |= peep->StaffOrders << WIDX_CHECKBOX_1;
859             break;
860         case StaffType::Security:
861             // Security guards don't have an options screen.
862             break;
863         case StaffType::Count:
864             break;
865     }
866 
867     window_staff_options_widgets[WIDX_BACKGROUND].right = w->width - 1;
868     window_staff_options_widgets[WIDX_BACKGROUND].bottom = w->height - 1;
869 
870     window_staff_options_widgets[WIDX_RESIZE].right = w->width - 1;
871     window_staff_options_widgets[WIDX_RESIZE].bottom = w->height - 1;
872 
873     window_staff_options_widgets[WIDX_TITLE].right = w->width - 2;
874     window_staff_options_widgets[WIDX_CLOSE].left = w->width - 13;
875     window_staff_options_widgets[WIDX_CLOSE].right = w->width - 3;
876 
877     window_align_tabs(w, WIDX_TAB_1, WIDX_TAB_3);
878 }
879 
880 /**
881  *
882  *  rct2: 0x006BDD91
883  */
window_staff_overview_invalidate(rct_window * w)884 void window_staff_overview_invalidate(rct_window* w)
885 {
886     ColourSchemeUpdateByClass(w, static_cast<rct_windowclass>(WC_STAFF));
887 
888     if (window_staff_page_widgets[w->page] != w->widgets)
889     {
890         w->widgets = window_staff_page_widgets[w->page];
891         WindowInitScrollWidgets(w);
892     }
893 
894     w->pressed_widgets |= 1ULL << (w->page + WIDX_TAB_1);
895 
896     const auto peep = GetStaff(w);
897     if (peep == nullptr)
898     {
899         return;
900     }
901     auto ft = Formatter::Common();
902     peep->FormatNameTo(ft);
903 
904     window_staff_overview_widgets[WIDX_BACKGROUND].right = w->width - 1;
905     window_staff_overview_widgets[WIDX_BACKGROUND].bottom = w->height - 1;
906 
907     window_staff_overview_widgets[WIDX_RESIZE].right = w->width - 1;
908     window_staff_overview_widgets[WIDX_RESIZE].bottom = w->height - 1;
909 
910     window_staff_overview_widgets[WIDX_TITLE].right = w->width - 2;
911 
912     window_staff_overview_widgets[WIDX_VIEWPORT].right = w->width - 26;
913     window_staff_overview_widgets[WIDX_VIEWPORT].bottom = w->height - 14;
914 
915     window_staff_overview_widgets[WIDX_BTM_LABEL].right = w->width - 26;
916     window_staff_overview_widgets[WIDX_BTM_LABEL].top = w->height - 13;
917     window_staff_overview_widgets[WIDX_BTM_LABEL].bottom = w->height - 3;
918 
919     window_staff_overview_widgets[WIDX_CLOSE].left = w->width - 13;
920     window_staff_overview_widgets[WIDX_CLOSE].right = w->width - 3;
921 
922     window_staff_overview_widgets[WIDX_PICKUP].left = w->width - 25;
923     window_staff_overview_widgets[WIDX_PICKUP].right = w->width - 2;
924 
925     window_staff_overview_widgets[WIDX_PATROL].left = w->width - 25;
926     window_staff_overview_widgets[WIDX_PATROL].right = w->width - 2;
927 
928     window_staff_overview_widgets[WIDX_RENAME].left = w->width - 25;
929     window_staff_overview_widgets[WIDX_RENAME].right = w->width - 2;
930 
931     window_staff_overview_widgets[WIDX_LOCATE].left = w->width - 25;
932     window_staff_overview_widgets[WIDX_LOCATE].right = w->width - 2;
933 
934     window_staff_overview_widgets[WIDX_FIRE].left = w->width - 25;
935     window_staff_overview_widgets[WIDX_FIRE].right = w->width - 2;
936 
937     window_align_tabs(w, WIDX_TAB_1, WIDX_TAB_3);
938 }
939 
940 /**
941  *
942  *  rct2: 0x6BDEAF
943  */
window_staff_overview_paint(rct_window * w,rct_drawpixelinfo * dpi)944 void window_staff_overview_paint(rct_window* w, rct_drawpixelinfo* dpi)
945 {
946     WindowDrawWidgets(w, dpi);
947     window_staff_overview_tab_paint(w, dpi);
948     window_staff_options_tab_paint(w, dpi);
949     window_staff_stats_tab_paint(w, dpi);
950 
951     // Draw the viewport no sound sprite
952     if (w->viewport != nullptr)
953     {
954         window_draw_viewport(dpi, w);
955         rct_viewport* viewport = w->viewport;
956         if (viewport->flags & VIEWPORT_FLAG_SOUND_ON)
957         {
958             gfx_draw_sprite(dpi, ImageId(SPR_HEARING_VIEWPORT), w->windowPos + ScreenCoordsXY{ 2, 2 });
959         }
960     }
961 
962     // Draw the centred label
963     const auto peep = GetStaff(w);
964     if (peep == nullptr)
965     {
966         return;
967     }
968     auto ft = Formatter();
969     peep->FormatActionTo(ft);
970     const auto& widget = w->widgets[WIDX_BTM_LABEL];
971     auto screenPos = w->windowPos + ScreenCoordsXY{ widget.midX(), widget.top };
972     int32_t width = widget.width();
973     DrawTextEllipsised(dpi, screenPos, width, STR_BLACK_STRING, ft, { TextAlignment::CENTRE });
974 }
975 
976 /**
977  *
978  *  rct2: 0x6BEC8F
979  */
window_staff_options_tab_paint(rct_window * w,rct_drawpixelinfo * dpi)980 void window_staff_options_tab_paint(rct_window* w, rct_drawpixelinfo* dpi)
981 {
982     if (w->disabled_widgets & (1ULL << WIDX_TAB_2))
983         return;
984 
985     int32_t image_id = SPR_TAB_STAFF_OPTIONS_0;
986     if (w->page == WINDOW_STAFF_OPTIONS)
987     {
988         image_id += (w->frame_no / 2) % 7;
989     }
990 
991     const auto& widget = w->widgets[WIDX_TAB_2];
992     auto screenCoords = w->windowPos + ScreenCoordsXY{ widget.left, widget.top };
993     gfx_draw_sprite(dpi, ImageId(image_id), screenCoords);
994 }
995 
996 /**
997  *
998  *  rct2: 0x6BECD3
999  */
window_staff_stats_tab_paint(rct_window * w,rct_drawpixelinfo * dpi)1000 void window_staff_stats_tab_paint(rct_window* w, rct_drawpixelinfo* dpi)
1001 {
1002     if (w->disabled_widgets & (1ULL << WIDX_TAB_3))
1003         return;
1004 
1005     int32_t image_id = SPR_TAB_STATS_0;
1006     if (w->page == WINDOW_STAFF_STATISTICS)
1007     {
1008         image_id += (w->frame_no / 4) % 7;
1009     }
1010 
1011     const auto& widget = w->widgets[WIDX_TAB_3];
1012     auto screenCoords = w->windowPos + ScreenCoordsXY{ widget.left, widget.top };
1013     gfx_draw_sprite(dpi, ImageId(image_id), screenCoords);
1014 }
1015 
1016 /**
1017  * Based on rct2: 0x6983dd in window_guest to be remerged into one when peep file added.
1018  */
window_staff_overview_tab_paint(rct_window * w,rct_drawpixelinfo * dpi)1019 void window_staff_overview_tab_paint(rct_window* w, rct_drawpixelinfo* dpi)
1020 {
1021     if (w->disabled_widgets & (1ULL << WIDX_TAB_1))
1022         return;
1023 
1024     const auto& widget = w->widgets[WIDX_TAB_1];
1025     int32_t width = widget.width() - 1;
1026     int32_t height = widget.height() - 1;
1027     auto screenCoords = w->windowPos + ScreenCoordsXY{ widget.left + 1, widget.top + 1 };
1028     if (w->page == WINDOW_STAFF_OVERVIEW)
1029         height++;
1030 
1031     rct_drawpixelinfo clip_dpi;
1032     if (!clip_drawpixelinfo(&clip_dpi, dpi, screenCoords, width, height))
1033     {
1034         return;
1035     }
1036 
1037     screenCoords = ScreenCoordsXY{ 14, 20 };
1038 
1039     const auto peep = GetStaff(w);
1040     if (peep == nullptr)
1041     {
1042         return;
1043     }
1044 
1045     if (peep->Is<Staff>() && peep->AssignedStaffType == StaffType::Entertainer)
1046         screenCoords.y++;
1047 
1048     int32_t ebx = GetPeepAnimation(peep->SpriteType).base_image + 1;
1049 
1050     int32_t eax = 0;
1051 
1052     if (w->page == WINDOW_STAFF_OVERVIEW)
1053     {
1054         eax = w->var_496;
1055         eax &= 0xFFFC;
1056     }
1057     ebx += eax;
1058 
1059     gfx_draw_sprite(&clip_dpi, ImageId(ebx, peep->TshirtColour, peep->TrousersColour), screenCoords);
1060 }
1061 
1062 /**
1063  *
1064  *  rct2: 0x6BE7C6
1065  */
window_staff_options_paint(rct_window * w,rct_drawpixelinfo * dpi)1066 void window_staff_options_paint(rct_window* w, rct_drawpixelinfo* dpi)
1067 {
1068     WindowDrawWidgets(w, dpi);
1069     window_staff_overview_tab_paint(w, dpi);
1070     window_staff_options_tab_paint(w, dpi);
1071     window_staff_stats_tab_paint(w, dpi);
1072 }
1073 
1074 /**
1075  *
1076  *  rct2: 0x6BEA86
1077  */
window_staff_stats_paint(rct_window * w,rct_drawpixelinfo * dpi)1078 void window_staff_stats_paint(rct_window* w, rct_drawpixelinfo* dpi)
1079 {
1080     WindowDrawWidgets(w, dpi);
1081     window_staff_overview_tab_paint(w, dpi);
1082     window_staff_options_tab_paint(w, dpi);
1083     window_staff_stats_tab_paint(w, dpi);
1084 
1085     const auto peep = GetStaff(w);
1086     if (peep == nullptr)
1087     {
1088         return;
1089     }
1090 
1091     auto screenCoords = w->windowPos
1092         + ScreenCoordsXY{ window_staff_stats_widgets[WIDX_RESIZE].left + 4, window_staff_stats_widgets[WIDX_RESIZE].top + 4 };
1093 
1094     if (!(gParkFlags & PARK_FLAGS_NO_MONEY))
1095     {
1096         auto ft = Formatter();
1097         ft.Add<money64>(GetStaffWage(peep->AssignedStaffType));
1098         DrawTextBasic(dpi, screenCoords, STR_STAFF_STAT_WAGES, ft);
1099         screenCoords.y += LIST_ROW_HEIGHT;
1100     }
1101 
1102     auto ft = Formatter();
1103     ft.Add<int32_t>(peep->GetHireDate());
1104     DrawTextBasic(dpi, screenCoords, STR_STAFF_STAT_EMPLOYED_FOR, ft);
1105     screenCoords.y += LIST_ROW_HEIGHT;
1106 
1107     switch (peep->AssignedStaffType)
1108     {
1109         case StaffType::Handyman:
1110             ft = Formatter();
1111             ft.Add<uint16_t>(peep->StaffLawnsMown);
1112             DrawTextBasic(dpi, screenCoords, STR_STAFF_STAT_LAWNS_MOWN, ft);
1113             screenCoords.y += LIST_ROW_HEIGHT;
1114 
1115             ft = Formatter();
1116             ft.Add<uint16_t>(peep->StaffGardensWatered);
1117             DrawTextBasic(dpi, screenCoords, STR_STAFF_STAT_GARDENS_WATERED, ft);
1118             screenCoords.y += LIST_ROW_HEIGHT;
1119 
1120             ft = Formatter();
1121             ft.Add<uint16_t>(peep->StaffLitterSwept);
1122             DrawTextBasic(dpi, screenCoords, STR_STAFF_STAT_LITTER_SWEPT, ft);
1123             screenCoords.y += LIST_ROW_HEIGHT;
1124 
1125             ft = Formatter();
1126             ft.Add<uint16_t>(peep->StaffBinsEmptied);
1127             DrawTextBasic(dpi, screenCoords, STR_STAFF_STAT_BINS_EMPTIED, ft);
1128             break;
1129         case StaffType::Mechanic:
1130             ft = Formatter();
1131             ft.Add<uint16_t>(peep->StaffRidesInspected);
1132             DrawTextBasic(dpi, screenCoords, STR_STAFF_STAT_RIDES_INSPECTED, ft);
1133             screenCoords.y += LIST_ROW_HEIGHT;
1134 
1135             ft = Formatter();
1136             ft.Add<uint16_t>(peep->StaffRidesFixed);
1137             DrawTextBasic(dpi, screenCoords, STR_STAFF_STAT_RIDES_FIXED, ft);
1138             break;
1139         case StaffType::Security:
1140             ft = Formatter();
1141             ft.Add<uint16_t>(peep->StaffVandalsStopped);
1142             DrawTextBasic(dpi, screenCoords, STR_STAFF_STAT_VANDALS_STOPPED, ft);
1143             break;
1144         case StaffType::Entertainer:
1145         case StaffType::Count:
1146             break;
1147     }
1148 }
1149 
1150 /**
1151  *
1152  *  rct2: 0x006BDFD8
1153  */
window_staff_overview_tool_update(rct_window * w,rct_widgetindex widgetIndex,const ScreenCoordsXY & screenCoords)1154 void window_staff_overview_tool_update(rct_window* w, rct_widgetindex widgetIndex, const ScreenCoordsXY& screenCoords)
1155 {
1156     if (widgetIndex != WIDX_PICKUP)
1157         return;
1158 
1159     map_invalidate_selection_rect();
1160 
1161     gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE;
1162 
1163     auto mapCoords = footpath_get_coordinates_from_pos({ screenCoords.x, screenCoords.y + 16 }, nullptr, nullptr);
1164     if (!mapCoords.IsNull())
1165     {
1166         gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE;
1167         gMapSelectType = MAP_SELECT_TYPE_FULL;
1168         gMapSelectPositionA = mapCoords;
1169         gMapSelectPositionB = mapCoords;
1170         map_invalidate_selection_rect();
1171     }
1172 
1173     gPickupPeepImage = UINT32_MAX;
1174 
1175     auto info = get_map_coordinates_from_pos(screenCoords, ViewportInteractionItemAll);
1176     if (info.SpriteType == ViewportInteractionItem::None)
1177         return;
1178 
1179     gPickupPeepX = screenCoords.x - 1;
1180     gPickupPeepY = screenCoords.y + 16;
1181     w->picked_peep_frame++;
1182     if (w->picked_peep_frame >= 48)
1183     {
1184         w->picked_peep_frame = 0;
1185     }
1186 
1187     const auto peep = GetStaff(w);
1188     if (peep == nullptr)
1189     {
1190         return;
1191     }
1192 
1193     uint32_t imageId = GetPeepAnimation(peep->SpriteType, PeepActionSpriteType::Ui).base_image;
1194     imageId += w->picked_peep_frame >> 2;
1195 
1196     imageId |= (peep->TshirtColour << 19) | (peep->TrousersColour << 24) | IMAGE_TYPE_REMAP | IMAGE_TYPE_REMAP_2_PLUS;
1197     gPickupPeepImage = imageId;
1198 }
1199 
1200 /**
1201  *
1202  *  rct2: 0x006BDFC3
1203  */
window_staff_overview_tool_down(rct_window * w,rct_widgetindex widgetIndex,const ScreenCoordsXY & screenCoords)1204 void window_staff_overview_tool_down(rct_window* w, rct_widgetindex widgetIndex, const ScreenCoordsXY& screenCoords)
1205 {
1206     if (widgetIndex == WIDX_PICKUP)
1207     {
1208         TileElement* tileElement;
1209         auto destCoords = footpath_get_coordinates_from_pos({ screenCoords.x, screenCoords.y + 16 }, nullptr, &tileElement);
1210 
1211         if (destCoords.IsNull())
1212             return;
1213 
1214         PeepPickupAction pickupAction{
1215             PeepPickupType::Place, w->number, { destCoords, tileElement->GetBaseZ() }, network_get_current_player_id()
1216         };
1217         pickupAction.SetCallback([](const GameAction* ga, const GameActions::Result* result) {
1218             if (result->Error != GameActions::Status::Ok)
1219                 return;
1220             tool_cancel();
1221             gPickupPeepImage = UINT32_MAX;
1222         });
1223         GameActions::Execute(&pickupAction);
1224     }
1225     else if (widgetIndex == WIDX_PATROL)
1226     {
1227         auto destCoords = footpath_get_coordinates_from_pos(screenCoords, nullptr, nullptr);
1228 
1229         if (destCoords.IsNull())
1230             return;
1231 
1232         auto staff = TryGetEntity<Staff>(w->number);
1233         if (staff == nullptr)
1234             return;
1235 
1236         if (staff->IsPatrolAreaSet(destCoords))
1237         {
1238             _staffPatrolAreaPaintValue = PatrolAreaValue::UNSET;
1239         }
1240         else
1241         {
1242             _staffPatrolAreaPaintValue = PatrolAreaValue::SET;
1243         }
1244         auto staffSetPatrolAreaAction = StaffSetPatrolAreaAction(
1245             w->number, destCoords,
1246             _staffPatrolAreaPaintValue == PatrolAreaValue::SET ? StaffSetPatrolAreaMode::Set : StaffSetPatrolAreaMode::Unset);
1247         GameActions::Execute(&staffSetPatrolAreaAction);
1248     }
1249 }
1250 
window_staff_overview_tool_drag(rct_window * w,rct_widgetindex widgetIndex,const ScreenCoordsXY & screenCoords)1251 void window_staff_overview_tool_drag(rct_window* w, rct_widgetindex widgetIndex, const ScreenCoordsXY& screenCoords)
1252 {
1253     if (widgetIndex != WIDX_PATROL)
1254         return;
1255 
1256     if (network_get_mode() != NETWORK_MODE_NONE)
1257         return;
1258 
1259     // This works only for singleplayer if the game_do_command can not be prevented
1260     // to send packets more often than patrol area is updated.
1261 
1262     if (_staffPatrolAreaPaintValue == PatrolAreaValue::NONE)
1263         return; // Do nothing if we do not have a paintvalue(this should never happen)
1264 
1265     auto destCoords = footpath_get_coordinates_from_pos(screenCoords, nullptr, nullptr);
1266 
1267     if (destCoords.IsNull())
1268         return;
1269 
1270     auto staff = TryGetEntity<Staff>(w->number);
1271     if (staff == nullptr)
1272         return;
1273 
1274     bool patrolAreaValue = staff->IsPatrolAreaSet(destCoords);
1275     if (_staffPatrolAreaPaintValue == PatrolAreaValue::SET && patrolAreaValue)
1276         return; // Since area is already the value we want, skip...
1277     if (_staffPatrolAreaPaintValue == PatrolAreaValue::UNSET && !patrolAreaValue)
1278         return; // Since area is already the value we want, skip...
1279 
1280     auto staffSetPatrolAreaAction = StaffSetPatrolAreaAction(
1281         w->number, destCoords,
1282         _staffPatrolAreaPaintValue == PatrolAreaValue::SET ? StaffSetPatrolAreaMode::Set : StaffSetPatrolAreaMode::Unset);
1283     GameActions::Execute(&staffSetPatrolAreaAction);
1284 }
1285 
window_staff_overview_tool_up(rct_window * w,rct_widgetindex widgetIndex,const ScreenCoordsXY & screenCoords)1286 void window_staff_overview_tool_up(rct_window* w, rct_widgetindex widgetIndex, const ScreenCoordsXY& screenCoords)
1287 {
1288     if (widgetIndex != WIDX_PATROL)
1289         return;
1290 
1291     _staffPatrolAreaPaintValue = PatrolAreaValue::NONE;
1292 }
1293 
1294 /**
1295  *
1296  *  rct2: 0x6BDFAE
1297  */
window_staff_overview_tool_abort(rct_window * w,rct_widgetindex widgetIndex)1298 void window_staff_overview_tool_abort(rct_window* w, rct_widgetindex widgetIndex)
1299 {
1300     if (widgetIndex == WIDX_PICKUP)
1301     {
1302         PeepPickupAction pickupAction{
1303             PeepPickupType::Cancel, w->number, { w->picked_peep_old_x, 0, 0 }, network_get_current_player_id()
1304         };
1305         GameActions::Execute(&pickupAction);
1306     }
1307     else if (widgetIndex == WIDX_PATROL)
1308     {
1309         hide_gridlines();
1310         gStaffDrawPatrolAreas = 0xFFFF;
1311         gfx_invalidate_screen();
1312     }
1313 }
1314 
1315 /* rct2: 0x6BDFED */
window_staff_overview_text_input(rct_window * w,rct_widgetindex widgetIndex,char * text)1316 void window_staff_overview_text_input(rct_window* w, rct_widgetindex widgetIndex, char* text)
1317 {
1318     if (widgetIndex != WIDX_RENAME)
1319         return;
1320 
1321     if (text == nullptr)
1322         return;
1323     staff_set_name(w->number, text);
1324 }
1325 
1326 /**
1327  *
1328  *  rct2: 0x006BE5FC
1329  */
window_staff_overview_viewport_rotate(rct_window * w)1330 void window_staff_overview_viewport_rotate(rct_window* w)
1331 {
1332     window_staff_viewport_init(w);
1333 }
1334 
1335 /**
1336  *
1337  *  rct2: 0x006BEDA3
1338  */
window_staff_viewport_init(rct_window * w)1339 void window_staff_viewport_init(rct_window* w)
1340 {
1341     if (w->page != WINDOW_STAFF_OVERVIEW)
1342         return;
1343 
1344     std::optional<Focus> focus;
1345 
1346     const auto peep = GetStaff(w);
1347     if (peep == nullptr)
1348     {
1349         return;
1350     }
1351 
1352     if (peep->State != PeepState::Picked)
1353     {
1354         focus = Focus(peep->sprite_index);
1355     }
1356 
1357     uint16_t viewport_flags;
1358 
1359     if (w->viewport != nullptr)
1360     {
1361         if (focus == w->focus)
1362             return;
1363 
1364         viewport_flags = w->viewport->flags;
1365         w->RemoveViewport();
1366     }
1367     else
1368     {
1369         viewport_flags = 0;
1370         if (gConfigGeneral.always_show_gridlines)
1371             viewport_flags |= VIEWPORT_FLAG_GRIDLINES;
1372     }
1373 
1374     window_event_invalidate_call(w);
1375 
1376     w->focus = focus;
1377 
1378     if (peep->State != PeepState::Picked)
1379     {
1380         if (w->viewport == nullptr)
1381         {
1382             const auto& view_widget = w->widgets[WIDX_VIEWPORT];
1383 
1384             auto screenPos = ScreenCoordsXY{ view_widget.left + 1 + w->windowPos.x, view_widget.top + 1 + w->windowPos.y };
1385             int32_t width = view_widget.width() - 1;
1386             int32_t height = view_widget.height() - 1;
1387 
1388             viewport_create(w, screenPos, width, height, focus.value());
1389             w->flags |= WF_NO_SCROLLING;
1390             w->Invalidate();
1391         }
1392     }
1393 
1394     if (w->viewport != nullptr)
1395         w->viewport->flags = viewport_flags;
1396     w->Invalidate();
1397 }
1398 
1399 /**
1400  * Handle the costume of staff member.
1401  * rct2: 0x006BE802
1402  */
window_staff_options_mousedown(rct_window * w,rct_widgetindex widgetIndex,rct_widget * widget)1403 void window_staff_options_mousedown(rct_window* w, rct_widgetindex widgetIndex, rct_widget* widget)
1404 {
1405     if (widgetIndex != WIDX_COSTUME_BTN)
1406     {
1407         return;
1408     }
1409 
1410     const auto peep = GetStaff(w);
1411     if (peep == nullptr)
1412     {
1413         return;
1414     }
1415     int32_t checkedIndex = -1;
1416     // This will be moved below where Items Checked is when all
1417     // of dropdown related functions are finished. This prevents
1418     // the dropdown from not working on first click.
1419     int32_t numCostumes = staff_get_available_entertainer_costume_list(_availableCostumes);
1420     for (int32_t i = 0; i < numCostumes; i++)
1421     {
1422         EntertainerCostume costume = _availableCostumes[i];
1423         if (peep->SpriteType == EntertainerCostumeToSprite(costume))
1424         {
1425             checkedIndex = i;
1426         }
1427         gDropdownItemsArgs[i] = StaffCostumeNames[static_cast<uint8_t>(costume)];
1428         gDropdownItemsFormat[i] = STR_DROPDOWN_MENU_LABEL;
1429     }
1430 
1431     // Get the dropdown box widget instead of button.
1432     widget--;
1433 
1434     auto dropdownPos = ScreenCoordsXY{ widget->left + w->windowPos.x, widget->top + w->windowPos.y };
1435     int32_t extray = widget->height() + 1;
1436     int32_t width = widget->width() - 3;
1437     WindowDropdownShowTextCustomWidth(dropdownPos, extray, w->colours[1], 0, Dropdown::Flag::StayOpen, numCostumes, width);
1438 
1439     // See above note.
1440     if (checkedIndex != -1)
1441     {
1442         Dropdown::SetChecked(checkedIndex, true);
1443     }
1444 }
1445 
1446 /**
1447  *
1448  *  rct2: 0x6BE809
1449  */
window_staff_options_dropdown(rct_window * w,rct_widgetindex widgetIndex,int32_t dropdownIndex)1450 void window_staff_options_dropdown(rct_window* w, rct_widgetindex widgetIndex, int32_t dropdownIndex)
1451 {
1452     if (widgetIndex != WIDX_COSTUME_BTN)
1453     {
1454         return;
1455     }
1456 
1457     if (dropdownIndex == -1)
1458         return;
1459 
1460     EntertainerCostume costume = _availableCostumes[dropdownIndex];
1461     auto staffSetCostumeAction = StaffSetCostumeAction(w->number, costume);
1462     GameActions::Execute(&staffSetCostumeAction);
1463 }
1464