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