OpenRCT2/src/openrct2-ui/windows/Staff.cpp

1262 lines
42 KiB
C++
Raw Normal View History

/*****************************************************************************
* Copyright (c) 2014-2024 OpenRCT2 developers
*
* For a complete list of all authors, please refer to contributors.md
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
*
* OpenRCT2 is licensed under the GNU General Public License version 3.
*****************************************************************************/
2018-06-22 23:21:44 +02:00
#include "../interface/Theme.h"
2017-10-06 23:21:00 +02:00
2018-06-22 23:21:44 +02:00
#include <openrct2-ui/interface/Dropdown.h>
2018-01-03 00:13:37 +01:00
#include <openrct2-ui/interface/Viewport.h>
#include <openrct2-ui/interface/Widget.h>
2018-06-22 23:21:44 +02:00
#include <openrct2-ui/windows/Window.h>
#include <openrct2/Context.h>
#include <openrct2/Game.h>
#include <openrct2/GameState.h>
2018-06-22 23:21:44 +02:00
#include <openrct2/Input.h>
Split actions hpp files into separate h and cpp files (#13548) * Split up SmallSceneryPlace/Remove Added undo function for Remove Scenery * Refactor: Balloon and Banner actions hpp=>h/cpp * Refactor: rename all action *.hpp files to *.cpp This is preparation for separation in later commits. Note that without the complete set of commits in this branch, the code will not build. * Refactor Clear, Climate, Custom, and Footpath actions hpp=>h/cpp * VSCode: add src subdirectories to includePath * Refactor Guest actions hpp=>h/cpp * Refactor Land actions hpp=>h/cpp * Refactor LargeScenery actions hpp=>h/cpp * Refactor Load, Maze, Network actions hpp=>h/cpp * Refactor Park actions hpp=>h/cpp * Refactor/style: move private function declarations in actions *.h Previous action .h files included private function declarations with private member variables, before public function declarations. This commit re-orders the header files to the following order: - public member variables - private member variables - public functions - private functions * Refactor Pause action hpp=>h/cpp * Refactor Peep, Place, Player actions hpp=>h/cpp * Refactor Ride actions hpp=>h/cpp * Refactor Scenario, Set*, Sign* actions hpp=>h/cpp * Refactor SmallScenerySetColourAction hpp=>h/cpp * Refactor Staff actions hpp=>h/cpp * Refactor Surface, Tile, Track* actions hpp=>h/cpp * Refactor Wall and Water actions hpp=>h/cpp * Fix various includes and other compile errors Update includes for tests. Move static function declarations to .h files Add explicit includes to various files that were previously implicit (the required header was a nested include in an action hpp file, and the action .h file does not include that header) Move RideSetStatus string enum to the cpp file to avoid unused imports * Xcode: modify project file for actions refactor * Cleanup whitespace and end-of-file newlines Co-authored-by: duncanspumpkin <duncans_pumpkin@hotmail.co.uk>
2020-12-10 07:39:10 +01:00
#include <openrct2/actions/PeepPickupAction.h>
#include <openrct2/actions/StaffSetCostumeAction.h>
2021-12-03 16:40:27 +01:00
#include <openrct2/actions/StaffSetNameAction.h>
Split actions hpp files into separate h and cpp files (#13548) * Split up SmallSceneryPlace/Remove Added undo function for Remove Scenery * Refactor: Balloon and Banner actions hpp=>h/cpp * Refactor: rename all action *.hpp files to *.cpp This is preparation for separation in later commits. Note that without the complete set of commits in this branch, the code will not build. * Refactor Clear, Climate, Custom, and Footpath actions hpp=>h/cpp * VSCode: add src subdirectories to includePath * Refactor Guest actions hpp=>h/cpp * Refactor Land actions hpp=>h/cpp * Refactor LargeScenery actions hpp=>h/cpp * Refactor Load, Maze, Network actions hpp=>h/cpp * Refactor Park actions hpp=>h/cpp * Refactor/style: move private function declarations in actions *.h Previous action .h files included private function declarations with private member variables, before public function declarations. This commit re-orders the header files to the following order: - public member variables - private member variables - public functions - private functions * Refactor Pause action hpp=>h/cpp * Refactor Peep, Place, Player actions hpp=>h/cpp * Refactor Ride actions hpp=>h/cpp * Refactor Scenario, Set*, Sign* actions hpp=>h/cpp * Refactor SmallScenerySetColourAction hpp=>h/cpp * Refactor Staff actions hpp=>h/cpp * Refactor Surface, Tile, Track* actions hpp=>h/cpp * Refactor Wall and Water actions hpp=>h/cpp * Fix various includes and other compile errors Update includes for tests. Move static function declarations to .h files Add explicit includes to various files that were previously implicit (the required header was a nested include in an action hpp file, and the action .h file does not include that header) Move RideSetStatus string enum to the cpp file to avoid unused imports * Xcode: modify project file for actions refactor * Cleanup whitespace and end-of-file newlines Co-authored-by: duncanspumpkin <duncans_pumpkin@hotmail.co.uk>
2020-12-10 07:39:10 +01:00
#include <openrct2/actions/StaffSetOrdersAction.h>
#include <openrct2/actions/StaffSetPatrolAreaAction.h>
2018-06-22 23:21:44 +02:00
#include <openrct2/config/Config.h>
2021-11-26 15:49:52 +01:00
#include <openrct2/entity/EntityRegistry.h>
2022-03-08 01:14:52 +01:00
#include <openrct2/entity/PatrolArea.h>
2021-11-25 22:47:24 +01:00
#include <openrct2/entity/Staff.h>
2021-12-12 00:06:06 +01:00
#include <openrct2/localisation/Formatter.h>
2018-01-06 18:32:25 +01:00
#include <openrct2/localisation/Localisation.h>
2018-06-22 23:21:44 +02:00
#include <openrct2/management/Finance.h>
#include <openrct2/network/network.h>
2017-10-06 23:21:00 +02:00
#include <openrct2/sprites.h>
2018-06-22 23:21:44 +02:00
#include <openrct2/windows/Intent.h>
2018-01-11 10:59:26 +01:00
#include <openrct2/world/Footpath.h>
2018-03-19 23:28:40 +01:00
#include <openrct2/world/Park.h>
namespace OpenRCT2::Ui::Windows
{
static constexpr StringId WINDOW_TITLE = STR_STRINGID;
static constexpr int32_t WW = 190;
static constexpr int32_t WH = 180;
enum WindowStaffPage
{
WINDOW_STAFF_OVERVIEW,
WINDOW_STAFF_OPTIONS,
WINDOW_STAFF_STATISTICS,
WINDOW_STAFF_PAGE_COUNT,
};
enum WindowStaffWidgetIdx
{
WIDX_BACKGROUND,
WIDX_TITLE,
WIDX_CLOSE,
WIDX_RESIZE,
WIDX_TAB_1,
WIDX_TAB_2,
WIDX_TAB_3,
WIDX_VIEWPORT = 7,
WIDX_BTM_LABEL,
WIDX_PICKUP,
WIDX_PATROL,
WIDX_RENAME,
WIDX_LOCATE,
WIDX_FIRE,
WIDX_CHECKBOX_1 = 7,
WIDX_CHECKBOX_2,
WIDX_CHECKBOX_3,
WIDX_CHECKBOX_4,
WIDX_COSTUME_BOX,
WIDX_COSTUME_BTN,
};
validate_global_widx(WC_PEEP, WIDX_PATROL);
validate_global_widx(WC_STAFF, WIDX_PICKUP);
// clang-format off
2020-07-30 22:45:40 +02:00
#define MAIN_STAFF_WIDGETS \
WINDOW_SHIM(WINDOW_TITLE, WW, WH), \
MakeWidget({ 0, 43}, {190, 137}, WindowWidgetType::Resize, WindowColour::Secondary), /* Resize */ \
MakeTab ({ 3, 17}, STR_STAFF_OVERVIEW_TIP ), /* Tab 1 */ \
MakeTab ({34, 17}, STR_STAFF_OPTIONS_TIP ), /* Tab 2 */ \
MakeTab ({65, 17}, STR_STAFF_STATS_TIP ) /* Tab 3 */
2020-07-30 22:45:40 +02:00
static Widget _staffOverviewWidgets[] = {
2020-07-30 22:45:40 +02:00
MAIN_STAFF_WIDGETS,
MakeWidget ({ 3, 47}, {162, 120}, WindowWidgetType::Viewport, WindowColour::Secondary ), // Viewport
MakeWidget ({ 3, WH - 13}, {162, 11}, WindowWidgetType::LabelCentred, WindowColour::Secondary ), // Label at bottom of viewport
MakeWidget ({WW - 25, 45}, { 24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, ImageId(SPR_PICKUP_BTN), STR_PICKUP_TIP ), // Pickup Button
MakeWidget ({WW - 25, 69}, { 24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, ImageId(SPR_PATROL_BTN), STR_SET_PATROL_TIP ), // Patrol Button
MakeWidget ({WW - 25, 93}, { 24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, ImageId(SPR_RENAME), STR_NAME_STAFF_TIP ), // Rename Button
MakeWidget ({WW - 25, 117}, { 24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, ImageId(SPR_LOCATE), STR_LOCATE_SUBJECT_TIP), // Locate Button
MakeWidget ({WW - 25, 141}, { 24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, ImageId(SPR_DEMOLISH), STR_FIRE_STAFF_TIP ), // Fire Button
kWidgetsEnd,
};
2014-10-10 13:52:40 +02:00
//0x9AF910
static Widget _staffOptionsWidgets[] = {
2020-07-30 22:45:40 +02:00
MAIN_STAFF_WIDGETS,
MakeWidget ({ 5, 50}, {180, 12}, WindowWidgetType::Checkbox, WindowColour::Secondary ), // Checkbox 1
MakeWidget ({ 5, 67}, {180, 12}, WindowWidgetType::Checkbox, WindowColour::Secondary ), // Checkbox 2
MakeWidget ({ 5, 84}, {180, 12}, WindowWidgetType::Checkbox, WindowColour::Secondary ), // Checkbox 3
MakeWidget ({ 5, 101}, {180, 12}, WindowWidgetType::Checkbox, WindowColour::Secondary ), // Checkbox 4
MakeWidget ({ 5, 50}, {180, 12}, WindowWidgetType::DropdownMenu, WindowColour::Secondary ), // Costume Dropdown
MakeWidget ({WW - 17, 51}, { 11, 10}, WindowWidgetType::Button, WindowColour::Secondary, STR_DROPDOWN_GLYPH, STR_SELECT_COSTUME_TIP), // Costume Dropdown Button
kWidgetsEnd,
2014-10-10 13:52:40 +02:00
};
// clang-format on
2014-10-10 13:52:40 +02:00
// 0x9AF9F4
static Widget _staffStatsWidgets[] = {
MAIN_STAFF_WIDGETS,
kWidgetsEnd,
};
2014-08-05 19:15:28 +02:00
static Widget* window_staff_page_widgets[] = {
_staffOverviewWidgets,
_staffOptionsWidgets,
_staffStatsWidgets,
};
class StaffWindow final : public Window
2018-06-22 23:21:44 +02:00
{
private:
EntertainerCostume _availableCostumes[EnumValue(EntertainerCostume::Count)]{};
uint16_t _tabAnimationOffset = 0;
int32_t _pickedPeepOldX = LOCATION_NULL;
public:
void Initialise(EntityId entityId)
{
number = entityId.ToUnderlying();
}
void OnOpen() override
{
SetPage(WINDOW_STAFF_OVERVIEW);
}
void OnClose() override
{
CancelTools();
}
void OnMouseUp(WidgetIndex widgetIndex) override
{
if (widgetIndex <= WIDX_TAB_3)
CommonMouseUp(widgetIndex);
else
{
switch (page)
{
case WINDOW_STAFF_OVERVIEW:
OverviewMouseUp(widgetIndex);
break;
case WINDOW_STAFF_OPTIONS:
OptionsMouseUp(widgetIndex);
}
}
}
void OnMouseDown(WidgetIndex widgetIndex) override
2018-06-22 23:21:44 +02:00
{
switch (page)
{
case WINDOW_STAFF_OVERVIEW:
OverviewOnMouseDown(widgetIndex);
break;
case WINDOW_STAFF_OPTIONS:
OptionsOnMouseDown(widgetIndex);
break;
}
}
void OnDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) override
{
switch (page)
{
case WINDOW_STAFF_OVERVIEW:
OverviewOnDropdown(widgetIndex, dropdownIndex);
break;
case WINDOW_STAFF_OPTIONS:
OptionsOnDropdown(widgetIndex, dropdownIndex);
break;
}
}
2014-08-05 19:15:28 +02:00
void OnPrepareDraw() override
{
CommonPrepareDrawBefore();
2015-10-20 20:16:30 +02:00
switch (page)
{
case WINDOW_STAFF_OVERVIEW:
OverviewPrepareDraw();
break;
case WINDOW_STAFF_OPTIONS:
OptionsPrepareDraw();
break;
}
CommonPrepareDrawAfter();
}
void OnDraw(DrawPixelInfo& dpi) override
{
DrawWidgets(dpi);
DrawTabImages(dpi);
switch (page)
{
case WINDOW_STAFF_OVERVIEW:
OverviewDraw(dpi);
break;
case WINDOW_STAFF_STATISTICS:
StatsDraw(dpi);
break;
}
}
void OnResize() override
{
switch (page)
{
case WINDOW_STAFF_OVERVIEW:
OverviewResize();
break;
case WINDOW_STAFF_OPTIONS:
OptionsResize();
break;
case WINDOW_STAFF_STATISTICS:
StatsResize();
break;
}
}
void OnUpdate() override
2018-06-22 23:21:44 +02:00
{
switch (page)
{
case WINDOW_STAFF_OVERVIEW:
OverviewUpdate();
break;
case WINDOW_STAFF_OPTIONS:
OptionsUpdate();
break;
case WINDOW_STAFF_STATISTICS:
StatsUpdate();
break;
}
2018-06-22 23:21:44 +02:00
}
void OnToolUpdate(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override
2019-07-21 21:44:19 +02:00
{
switch (page)
{
case WINDOW_STAFF_OVERVIEW:
OverviewToolUpdate(widgetIndex, screenCoords);
break;
}
2019-07-21 21:44:19 +02:00
}
void OnToolDown(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override
{
switch (page)
{
case WINDOW_STAFF_OVERVIEW:
OverviewToolDown(widgetIndex, screenCoords);
break;
}
}
void OnToolAbort(WidgetIndex widgetIndex) override
{
switch (page)
{
case WINDOW_STAFF_OVERVIEW:
OverviewToolAbort(widgetIndex);
break;
}
}
void OnViewportRotate() override
{
switch (page)
{
case WINDOW_STAFF_OVERVIEW:
OverviewViewportRotate();
break;
}
}
void OnTextInput(WidgetIndex widgetIndex, std::string_view text) override
{
switch (page)
{
case WINDOW_STAFF_OVERVIEW:
OverviewTextInput(widgetIndex, text);
break;
}
}
private:
#pragma region Common events
void CommonMouseUp(WidgetIndex widgetIndex)
{
switch (widgetIndex)
{
case WIDX_CLOSE:
Close();
break;
case WIDX_TAB_1:
case WIDX_TAB_2:
case WIDX_TAB_3:
SetPage(widgetIndex - WIDX_TAB_1);
break;
}
}
void CommonPrepareDrawBefore()
2018-06-22 23:21:44 +02:00
{
ColourSchemeUpdateByClass(this, static_cast<WindowClass>(WindowClass::Staff));
if (window_staff_page_widgets[page] != widgets)
{
widgets = window_staff_page_widgets[page];
InitScrollWidgets();
}
SetPressedTab();
DisableWidgets();
auto staff = GetStaff();
if (staff == nullptr)
{
return;
}
auto ft = Formatter::Common();
staff->FormatNameTo(ft);
ResizeFrameWithPage();
}
2014-08-05 19:15:28 +02:00
void CommonPrepareDrawAfter()
{
WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_3);
}
#pragma endregion
#pragma region Overview tab events
void OverviewMouseUp(WidgetIndex widgetIndex)
{
auto staff = GetStaff();
if (staff == nullptr)
{
return;
}
2014-08-05 19:15:28 +02:00
switch (widgetIndex)
{
case WIDX_PICKUP:
{
_pickedPeepOldX = staff->x;
CoordsXYZ nullLoc{};
nullLoc.SetNull();
PeepPickupAction pickupAction{ PeepPickupType::Pickup, EntityId::FromUnderlying(number), nullLoc,
NetworkGetCurrentPlayerId() };
pickupAction.SetCallback([peepnum = number](const GameAction* ga, const GameActions::Result* result) {
if (result->Error != GameActions::Status::Ok)
return;
WindowBase* wind = WindowFindByNumber(WindowClass::Peep, peepnum);
if (wind != nullptr)
{
ToolSet(*wind, WC_STAFF__WIDX_PICKUP, Tool::Picker);
}
});
GameActions::Execute(&pickupAction);
}
break;
case WIDX_FIRE:
{
auto intent = Intent(WindowClass::FirePrompt);
intent.PutExtra(INTENT_EXTRA_PEEP, staff);
ContextOpenIntent(&intent);
break;
}
case WIDX_RENAME:
{
auto peepName = staff->GetName();
WindowTextInputRawOpen(
this, widgetIndex, STR_STAFF_TITLE_STAFF_MEMBER_NAME, STR_STAFF_PROMPT_ENTER_NAME, {}, peepName.c_str(),
32);
break;
}
}
}
void OverviewOnMouseDown(WidgetIndex widgetIndex)
2018-06-22 23:21:44 +02:00
{
Widget* widget = &widgets[widgetIndex];
switch (widgetIndex)
{
case WIDX_LOCATE:
ShowLocateDropdown(widget);
break;
case WIDX_PATROL:
2022-03-11 21:26:29 +01:00
{
// Dropdown names
gDropdownItems[0].Format = STR_SET_PATROL_AREA;
gDropdownItems[1].Format = STR_CLEAR_PATROL_AREA;
auto ddPos = ScreenCoordsXY{ widget->left + windowPos.x, widget->top + windowPos.y };
int32_t extraHeight = widget->height() + 1;
WindowDropdownShowText(ddPos, extraHeight, colours[1], 0, 2);
gDropdownDefaultIndex = 0;
auto staff = GetStaff();
if (staff == nullptr)
{
return;
}
// Disable clear patrol area if no area is set.
if (!staff->HasPatrolArea())
{
Dropdown::SetDisabled(1, true);
}
}
}
}
void OverviewOnDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex)
{
switch (widgetIndex)
{
case WIDX_LOCATE:
{
if (dropdownIndex == 0)
{
ScrollToViewport();
}
else if (dropdownIndex == 1)
{
FollowPeep();
}
break;
2022-03-11 21:26:29 +01:00
}
case WIDX_PATROL:
{
// Clear patrol
if (dropdownIndex == 1)
{
auto staff = GetStaff();
if (staff == nullptr)
{
return;
}
WindowCloseByClass(WindowClass::PatrolArea);
auto staffSetPatrolAreaAction = StaffSetPatrolAreaAction(
staff->Id, {}, StaffSetPatrolAreaMode::ClearAll);
GameActions::Execute(&staffSetPatrolAreaAction);
}
else
{
auto staffId = EntityId::FromUnderlying(number);
if (WindowPatrolAreaGetCurrentStaffId() == staffId)
{
WindowCloseByClass(WindowClass::PatrolArea);
}
else
{
PatrolAreaOpen(staffId);
}
}
break;
}
}
}
2014-08-05 19:15:28 +02:00
void OverviewPrepareDraw()
{
auto staff = GetStaff();
if (staff == nullptr)
{
return;
}
widgets[WIDX_VIEWPORT].right = width - 26;
widgets[WIDX_VIEWPORT].bottom = height - 14;
widgets[WIDX_BTM_LABEL].right = width - 26;
widgets[WIDX_BTM_LABEL].top = height - 13;
widgets[WIDX_BTM_LABEL].bottom = height - 3;
widgets[WIDX_PICKUP].left = width - 25;
widgets[WIDX_PICKUP].right = width - 2;
2014-08-05 19:15:28 +02:00
SetWidgetPressed(WIDX_PATROL, WindowPatrolAreaGetCurrentStaffId() == staff->Id);
2014-08-05 19:15:28 +02:00
widgets[WIDX_PATROL].left = width - 25;
widgets[WIDX_PATROL].right = width - 2;
2014-08-05 19:15:28 +02:00
widgets[WIDX_RENAME].left = width - 25;
widgets[WIDX_RENAME].right = width - 2;
2014-08-05 19:15:28 +02:00
widgets[WIDX_LOCATE].left = width - 25;
widgets[WIDX_LOCATE].right = width - 2;
2014-08-05 19:15:28 +02:00
widgets[WIDX_FIRE].left = width - 25;
widgets[WIDX_FIRE].right = width - 2;
}
void OverviewDraw(DrawPixelInfo& dpi)
{
// Draw the viewport no sound sprite
if (viewport != nullptr)
{
WindowDrawViewport(dpi, *this);
if (viewport->flags & VIEWPORT_FLAG_SOUND_ON)
{
GfxDrawSprite(dpi, ImageId(SPR_HEARING_VIEWPORT), WindowGetViewportSoundIconPos(*this));
}
}
// Draw the centred label
auto staff = GetStaff();
if (staff == nullptr)
{
return;
}
auto ft = Formatter();
staff->FormatActionTo(ft);
const auto& widget = widgets[WIDX_BTM_LABEL];
auto screenPos = windowPos + ScreenCoordsXY{ widget.midX(), widget.top };
int32_t widgetWidth = widget.width();
DrawTextEllipsised(dpi, screenPos, widgetWidth, STR_BLACK_STRING, ft, { TextAlignment::CENTRE });
}
void DrawOverviewTabImage(DrawPixelInfo& dpi)
{
if (IsWidgetDisabled(WIDX_TAB_1))
return;
const auto& widget = widgets[WIDX_TAB_1];
int32_t widgetWidth = widget.width() - 1;
int32_t widgetHeight = widget.height() - 1;
auto screenCoords = windowPos + ScreenCoordsXY{ widget.left + 1, widget.top + 1 };
if (page == WINDOW_STAFF_OVERVIEW)
widgetHeight++;
DrawPixelInfo clip_dpi;
if (!ClipDrawPixelInfo(clip_dpi, dpi, screenCoords, widgetWidth, widgetHeight))
{
return;
}
screenCoords = ScreenCoordsXY{ 14, 20 };
auto staff = GetStaff();
if (staff == nullptr)
{
return;
}
if (staff->Is<Staff>() && staff->AssignedStaffType == StaffType::Entertainer)
screenCoords.y++;
int32_t imageIndex = GetPeepAnimation(staff->SpriteType).base_image + 1;
int32_t offset = 0;
if (page == WINDOW_STAFF_OVERVIEW)
{
offset = _tabAnimationOffset;
offset = Floor2(offset, 4);
}
imageIndex += offset;
GfxDrawSprite(clip_dpi, ImageId(imageIndex, staff->TshirtColour, staff->TrousersColour), screenCoords);
}
void OverviewResize()
{
min_width = WW;
max_width = 500;
min_height = WH;
max_height = 450;
if (width < min_width)
{
width = min_width;
Invalidate();
}
if (width > max_width)
{
Invalidate();
width = max_width;
}
if (height < min_height)
{
height = min_height;
Invalidate();
}
if (height > max_height)
{
Invalidate();
height = max_height;
}
if (viewport != nullptr)
{
int32_t newWidth = width - 30;
int32_t newHeight = height - 62;
// Update the viewport size
if (viewport->width != newWidth || viewport->height != newHeight)
{
viewport->width = newWidth;
viewport->height = newHeight;
viewport->view_width = viewport->zoom.ApplyTo(newWidth);
viewport->view_height = viewport->zoom.ApplyTo(newHeight);
}
}
ViewportInit();
}
void OverviewUpdate()
{
_tabAnimationOffset++;
_tabAnimationOffset %= 24;
// Update pickup animation, can only happen in this tab.
picked_peep_frame++;
picked_peep_frame %= 48;
InvalidateWidget(WIDX_TAB_1);
}
void OverviewToolUpdate(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords)
{
if (widgetIndex != WIDX_PICKUP)
return;
MapInvalidateSelectionRect();
gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE;
2015-10-20 20:16:30 +02:00
auto mapCoords = FootpathGetCoordinatesFromPos({ screenCoords.x, screenCoords.y + 16 }, nullptr, nullptr);
if (!mapCoords.IsNull())
{
gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE;
gMapSelectType = MAP_SELECT_TYPE_FULL;
gMapSelectPositionA = mapCoords;
gMapSelectPositionB = mapCoords;
MapInvalidateSelectionRect();
}
gPickupPeepImage = ImageId();
auto info = GetMapCoordinatesFromPos(screenCoords, ViewportInteractionItemAll);
if (info.SpriteType == ViewportInteractionItem::None)
return;
gPickupPeepX = screenCoords.x - 1;
gPickupPeepY = screenCoords.y + 16;
auto staff = GetStaff();
if (staff == nullptr)
{
return;
}
auto baseImageId = GetPeepAnimation(staff->SpriteType, PeepActionSpriteType::Ui).base_image;
baseImageId += picked_peep_frame >> 2;
gPickupPeepImage = ImageId(baseImageId, staff->TshirtColour, staff->TrousersColour);
}
void OverviewToolDown(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords)
{
if (widgetIndex != WIDX_PICKUP)
return;
const auto staffEntityId = EntityId::FromUnderlying(number);
TileElement* tileElement;
auto destCoords = FootpathGetCoordinatesFromPos({ screenCoords.x, screenCoords.y + 16 }, nullptr, &tileElement);
if (destCoords.IsNull())
return;
PeepPickupAction pickupAction{
PeepPickupType::Place, staffEntityId, { destCoords, tileElement->GetBaseZ() }, NetworkGetCurrentPlayerId()
};
pickupAction.SetCallback([](const GameAction* ga, const GameActions::Result* result) {
if (result->Error != GameActions::Status::Ok)
return;
ToolCancel();
gPickupPeepImage = ImageId();
});
GameActions::Execute(&pickupAction);
}
void OverviewToolAbort(WidgetIndex widgetIndex)
{
if (widgetIndex != WIDX_PICKUP)
return;
PeepPickupAction pickupAction{
PeepPickupType::Cancel, EntityId::FromUnderlying(number), { _pickedPeepOldX, 0, 0 }, NetworkGetCurrentPlayerId()
};
GameActions::Execute(&pickupAction);
}
void OverviewViewportRotate()
2018-06-22 23:21:44 +02:00
{
ViewportInit();
}
void OverviewTextInput(WidgetIndex widgetIndex, std::string_view text)
{
if (widgetIndex != WIDX_RENAME)
return;
if (text.empty())
return;
auto gameAction = StaffSetNameAction(EntityId::FromUnderlying(number), std::string{ text });
GameActions::Execute(&gameAction);
}
#pragma endregion
#pragma region Options tab events
void OptionsMouseUp(WidgetIndex widgetIndex)
{
switch (widgetIndex)
{
case WIDX_CHECKBOX_1:
case WIDX_CHECKBOX_2:
case WIDX_CHECKBOX_3:
case WIDX_CHECKBOX_4:
SetOrder(widgetIndex - WIDX_CHECKBOX_1);
break;
}
}
void OptionsOnMouseDown(WidgetIndex widgetIndex)
{
if (widgetIndex != WIDX_COSTUME_BTN)
{
return;
}
auto* ddWidget = &widgets[WIDX_COSTUME_BOX];
auto staff = GetStaff();
if (staff == nullptr)
{
return;
}
int32_t checkedIndex = -1;
// This will be moved below where Items Checked is when all
// of dropdown related functions are finished. This prevents
// the dropdown from not working on first click.
int32_t numCostumes = StaffGetAvailableEntertainerCostumeList(_availableCostumes);
for (int32_t i = 0; i < numCostumes; i++)
{
EntertainerCostume costume = _availableCostumes[i];
if (staff->SpriteType == EntertainerCostumeToSprite(costume))
{
checkedIndex = i;
}
gDropdownItems[i].Args = StaffCostumeNames[EnumValue(costume)];
gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL;
}
auto ddPos = ScreenCoordsXY{ ddWidget->left + windowPos.x, ddWidget->top + windowPos.y };
int32_t ddHeight = ddWidget->height() + 1;
int32_t ddWidth = ddWidget->width() - 3;
WindowDropdownShowTextCustomWidth(ddPos, ddHeight, colours[1], 0, Dropdown::Flag::StayOpen, numCostumes, ddWidth);
// See above note.
if (checkedIndex != -1)
{
Dropdown::SetChecked(checkedIndex, true);
}
}
void OptionsOnDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex)
{
if (widgetIndex != WIDX_COSTUME_BTN)
{
return;
}
if (dropdownIndex == -1)
return;
EntertainerCostume costume = _availableCostumes[dropdownIndex];
auto staffSetCostumeAction = StaffSetCostumeAction(EntityId::FromUnderlying(number), costume);
GameActions::Execute(&staffSetCostumeAction);
}
void OptionsPrepareDraw()
{
auto staff = GetStaff();
if (staff == nullptr)
{
return;
}
switch (staff->AssignedStaffType)
{
case StaffType::Entertainer:
widgets[WIDX_CHECKBOX_1].type = WindowWidgetType::Empty;
widgets[WIDX_CHECKBOX_2].type = WindowWidgetType::Empty;
widgets[WIDX_CHECKBOX_3].type = WindowWidgetType::Empty;
widgets[WIDX_CHECKBOX_4].type = WindowWidgetType::Empty;
widgets[WIDX_COSTUME_BOX].type = WindowWidgetType::DropdownMenu;
widgets[WIDX_COSTUME_BTN].type = WindowWidgetType::Button;
widgets[WIDX_COSTUME_BOX].text = StaffCostumeNames[EnumValue(staff->SpriteType) - 4];
break;
case StaffType::Handyman:
widgets[WIDX_CHECKBOX_1].type = WindowWidgetType::Checkbox;
widgets[WIDX_CHECKBOX_1].text = STR_STAFF_OPTION_SWEEP_FOOTPATHS;
widgets[WIDX_CHECKBOX_2].type = WindowWidgetType::Checkbox;
widgets[WIDX_CHECKBOX_2].text = STR_STAFF_OPTION_WATER_GARDENS;
widgets[WIDX_CHECKBOX_3].type = WindowWidgetType::Checkbox;
widgets[WIDX_CHECKBOX_3].text = STR_STAFF_OPTION_EMPTY_LITTER;
widgets[WIDX_CHECKBOX_4].type = WindowWidgetType::Checkbox;
widgets[WIDX_CHECKBOX_4].text = STR_STAFF_OPTION_MOW_GRASS;
widgets[WIDX_COSTUME_BOX].type = WindowWidgetType::Empty;
widgets[WIDX_COSTUME_BTN].type = WindowWidgetType::Empty;
OptionsSetCheckboxValues();
break;
case StaffType::Mechanic:
widgets[WIDX_CHECKBOX_1].type = WindowWidgetType::Checkbox;
widgets[WIDX_CHECKBOX_1].text = STR_INSPECT_RIDES;
widgets[WIDX_CHECKBOX_2].type = WindowWidgetType::Checkbox;
widgets[WIDX_CHECKBOX_2].text = STR_FIX_RIDES;
widgets[WIDX_CHECKBOX_3].type = WindowWidgetType::Empty;
widgets[WIDX_CHECKBOX_4].type = WindowWidgetType::Empty;
widgets[WIDX_COSTUME_BOX].type = WindowWidgetType::Empty;
widgets[WIDX_COSTUME_BTN].type = WindowWidgetType::Empty;
widgets[WIDX_COSTUME_BTN].type = WindowWidgetType::Empty;
OptionsSetCheckboxValues();
break;
case StaffType::Security:
// Security guards don't have an options screen.
break;
case StaffType::Count:
break;
}
}
void OptionsSetCheckboxValues()
{
SetCheckboxValue(WIDX_CHECKBOX_1, false);
SetCheckboxValue(WIDX_CHECKBOX_2, false);
SetCheckboxValue(WIDX_CHECKBOX_3, false);
SetCheckboxValue(WIDX_CHECKBOX_4, false);
auto staff = GetStaff();
if (staff == nullptr)
{
return;
}
auto staffOrders = staff->StaffOrders;
for (auto index = UtilBitScanForward(staffOrders); index != -1; index = UtilBitScanForward(staffOrders))
{
staffOrders &= ~(1 << index);
SetCheckboxValue(WIDX_CHECKBOX_1 + index, true);
}
}
void OptionsResize()
{
min_width = 190;
max_width = 190;
min_height = 126;
max_height = 126;
if (width < min_width)
{
width = min_width;
Invalidate();
}
if (width > max_width)
{
Invalidate();
width = max_width;
}
if (height < min_height)
{
height = min_height;
Invalidate();
}
if (height > max_height)
{
Invalidate();
height = max_height;
}
}
void OptionsUpdate()
{
frame_no++;
InvalidateWidget(WIDX_TAB_2);
}
#pragma endregion
#pragma region Statistics tab events
void StatsDraw(DrawPixelInfo& dpi)
{
auto staff = GetStaff();
if (staff == nullptr)
{
return;
}
auto screenCoords = windowPos + ScreenCoordsXY{ widgets[WIDX_RESIZE].left + 4, widgets[WIDX_RESIZE].top + 4 };
if (!(GetGameState().Park.Flags & PARK_FLAGS_NO_MONEY))
{
auto ft = Formatter();
ft.Add<money64>(GetStaffWage(staff->AssignedStaffType));
DrawTextBasic(dpi, screenCoords, STR_STAFF_STAT_WAGES, ft);
screenCoords.y += LIST_ROW_HEIGHT;
}
auto ft = Formatter();
ft.Add<int32_t>(staff->GetHireDate());
DrawTextBasic(dpi, screenCoords, STR_STAFF_STAT_EMPLOYED_FOR, ft);
screenCoords.y += LIST_ROW_HEIGHT;
switch (staff->AssignedStaffType)
{
case StaffType::Handyman:
ft = Formatter();
ft.Add<uint32_t>(staff->StaffLawnsMown);
DrawTextBasic(dpi, screenCoords, STR_STAFF_STAT_LAWNS_MOWN, ft);
screenCoords.y += LIST_ROW_HEIGHT;
ft = Formatter();
ft.Add<uint32_t>(staff->StaffGardensWatered);
DrawTextBasic(dpi, screenCoords, STR_STAFF_STAT_GARDENS_WATERED, ft);
screenCoords.y += LIST_ROW_HEIGHT;
ft = Formatter();
ft.Add<uint32_t>(staff->StaffLitterSwept);
DrawTextBasic(dpi, screenCoords, STR_STAFF_STAT_LITTER_SWEPT, ft);
screenCoords.y += LIST_ROW_HEIGHT;
ft = Formatter();
ft.Add<uint32_t>(staff->StaffBinsEmptied);
DrawTextBasic(dpi, screenCoords, STR_STAFF_STAT_BINS_EMPTIED, ft);
break;
case StaffType::Mechanic:
ft = Formatter();
ft.Add<uint32_t>(staff->StaffRidesInspected);
DrawTextBasic(dpi, screenCoords, STR_STAFF_STAT_RIDES_INSPECTED, ft);
screenCoords.y += LIST_ROW_HEIGHT;
ft = Formatter();
ft.Add<uint32_t>(staff->StaffRidesFixed);
DrawTextBasic(dpi, screenCoords, STR_STAFF_STAT_RIDES_FIXED, ft);
break;
case StaffType::Security:
ft = Formatter();
ft.Add<uint32_t>(staff->StaffVandalsStopped);
DrawTextBasic(dpi, screenCoords, STR_STAFF_STAT_VANDALS_STOPPED, ft);
break;
case StaffType::Entertainer:
case StaffType::Count:
break;
}
}
void StatsResize()
{
min_width = 190;
max_width = 190;
min_height = 126;
max_height = 126;
if (width < min_width)
{
width = min_width;
Invalidate();
}
if (width > max_width)
{
Invalidate();
width = max_width;
}
if (height < min_height)
{
height = min_height;
Invalidate();
}
if (height > max_height)
{
Invalidate();
height = max_height;
}
}
void StatsUpdate()
{
frame_no++;
InvalidateWidget(WIDX_TAB_3);
auto staff = GetStaff();
if (staff == nullptr)
{
return;
}
if (staff->WindowInvalidateFlags & PEEP_INVALIDATE_STAFF_STATS)
{
staff->WindowInvalidateFlags &= ~PEEP_INVALIDATE_STAFF_STATS;
Invalidate();
}
}
#pragma endregion
void DisableWidgets()
{
const auto staff = GetStaff();
if (staff == nullptr)
{
return;
}
for (WidgetIndex widgetIndex = WIDX_TAB_1; widgets[widgetIndex].type != kWidgetsEnd.type; widgetIndex++)
{
SetWidgetDisabled(widgetIndex, false);
}
if (staff->AssignedStaffType == StaffType::Security)
{
SetWidgetDisabled(WIDX_TAB_2, true);
}
if (page == WINDOW_STAFF_OVERVIEW)
{
if (staff->CanBePickedUp())
{
SetWidgetDisabled(WIDX_PICKUP, false);
}
else
{
SetWidgetDisabled(WIDX_PICKUP, true);
}
SetWidgetDisabled(WIDX_FIRE, staff->State == PeepState::Fixing || staff->State == PeepState::Inspecting);
}
}
void CancelTools()
{
if (InputTestFlag(INPUT_FLAG_TOOL_ACTIVE))
{
if (number == gCurrentToolWidget.window_number && classification == gCurrentToolWidget.window_classification)
ToolCancel();
}
}
void SetPage(int32_t pageNum)
{
CancelTools();
int32_t listen = 0;
if (page == WINDOW_STAFF_OVERVIEW && viewport != nullptr)
{
if (!(viewport->flags & VIEWPORT_FLAG_SOUND_ON))
listen = 1;
}
page = pageNum;
frame_no = 0;
pressed_widgets = 0;
hold_down_widgets = 0;
widgets = window_staff_page_widgets[page];
RemoveViewport();
Invalidate();
OnResize();
OnPrepareDraw();
InitScrollWidgets();
ViewportInit();
Invalidate();
if (listen && viewport != nullptr)
viewport->flags |= VIEWPORT_FLAG_SOUND_ON;
}
void SetPressedTab()
{
for (int32_t i = 0; i < WINDOW_STAFF_PAGE_COUNT; i++)
SetWidgetPressed((WIDX_TAB_1 + i), false);
SetWidgetPressed(WIDX_TAB_1 + page, true);
}
void SetOrder(int32_t orderId)
{
auto staff = GetStaff();
if (staff == nullptr)
{
return;
}
uint8_t newOrders = staff->StaffOrders ^ (1 << orderId);
auto staffSetOrdersAction = StaffSetOrdersAction(EntityId::FromUnderlying(number), newOrders);
GameActions::Execute(&staffSetOrdersAction);
}
2014-10-12 12:34:08 +02:00
void ViewportInit()
{
if (page != WINDOW_STAFF_OVERVIEW)
return;
2014-10-12 12:34:08 +02:00
auto staff = GetStaff();
if (staff == nullptr)
{
return;
}
2021-12-03 16:40:27 +01:00
std::optional<Focus> tempFocus;
if (staff->State != PeepState::Picked)
{
tempFocus = Focus(staff->Id);
}
2014-10-12 12:34:08 +02:00
uint16_t viewport_flags;
2014-10-12 12:34:08 +02:00
if (viewport != nullptr)
{
if (tempFocus == focus)
return;
viewport_flags = viewport->flags;
RemoveViewport();
}
else
{
viewport_flags = 0;
if (gConfigGeneral.AlwaysShowGridlines)
viewport_flags |= VIEWPORT_FLAG_GRIDLINES;
}
OnPrepareDraw();
focus = tempFocus;
if (staff->State != PeepState::Picked)
{
if (viewport == nullptr)
{
const auto& viewWidget = widgets[WIDX_VIEWPORT];
auto screenPos = ScreenCoordsXY{ viewWidget.left + 1 + windowPos.x, viewWidget.top + 1 + windowPos.y };
int32_t viewportWidth = viewWidget.width() - 1;
int32_t viewportHeight = viewWidget.height() - 1;
ViewportCreate(this, screenPos, viewportWidth, viewportHeight, focus.value());
flags |= WF_NO_SCROLLING;
Invalidate();
}
}
if (viewport != nullptr)
viewport->flags = viewport_flags;
Invalidate();
}
void ShowLocateDropdown(Widget* widget)
{
gDropdownItems[0].Format = STR_LOCATE_SUBJECT_TIP;
gDropdownItems[1].Format = STR_FOLLOW_SUBJECT_TIP;
WindowDropdownShowText(
{ windowPos.x + widget->left, windowPos.y + widget->top }, widget->height() + 1, colours[1], 0, 2);
gDropdownDefaultIndex = 0;
}
void FollowPeep()
2018-06-22 23:21:44 +02:00
{
WindowBase* main = WindowGetMain();
WindowFollowSprite(*main, EntityId::FromUnderlying(number));
}
void DrawTabImages(DrawPixelInfo& dpi)
{
DrawOverviewTabImage(dpi);
DrawTabImage(dpi, WINDOW_STAFF_OPTIONS, SPR_TAB_STAFF_OPTIONS_0);
DrawTabImage(dpi, WINDOW_STAFF_STATISTICS, SPR_TAB_STATS_0);
}
void DrawTabImage(DrawPixelInfo& dpi, int32_t p, int32_t baseImageId)
{
WidgetIndex widgetIndex = WIDX_TAB_1 + p;
Widget* widget = &widgets[widgetIndex];
auto screenCoords = windowPos + ScreenCoordsXY{ widget->left, widget->top };
if (!IsWidgetDisabled(widgetIndex))
{
if (page == p)
{
int32_t frame = frame_no / TabAnimationDivisor[page - 1];
baseImageId += (frame % TabAnimationFrames);
}
// Draw normal, enabled sprite.
GfxDrawSprite(dpi, ImageId(baseImageId), screenCoords);
}
}
Staff* GetStaff()
2018-06-22 23:21:44 +02:00
{
return GetEntity<Staff>(EntityId::FromUnderlying(number));
}
static constexpr int32_t TabAnimationDivisor[] = {
2, // WINDOW_STAFF_OPTIONS,
4, // WINDOW_STAFF_STATISTICS,
};
2014-10-12 12:34:08 +02:00
static constexpr int32_t TabAnimationFrames = 7;
};
WindowBase* StaffOpen(Peep* peep)
{
auto w = static_cast<StaffWindow*>(WindowBringToFrontByNumber(WindowClass::Peep, peep->Id.ToUnderlying()));
if (w != nullptr)
return w;
2014-10-12 12:34:08 +02:00
w = WindowCreate<StaffWindow>(WindowClass::Peep, WW, WH, WF_10 | WF_RESIZABLE);
2014-10-12 12:34:08 +02:00
if (w == nullptr)
return nullptr;
if (w != nullptr)
w->Initialise(peep->Id);
return w;
}
} // namespace OpenRCT2::Ui::Windows