diff --git a/data/language/en-GB.txt b/data/language/en-GB.txt
index ed9b41a46a..ca7d4cbfa5 100644
--- a/data/language/en-GB.txt
+++ b/data/language/en-GB.txt
@@ -3657,6 +3657,8 @@ STR_6465 :Intensity: {COMMA2DP32}
STR_6466 :Nausea
STR_6467 :Nausea: {COMMA2DP32}
STR_6468 :Not Yet Known
+STR_6469 :Adjust smaller area of patrol area
+STR_6470 :Adjust larger area of patrol area
#############
# Scenarios #
diff --git a/src/openrct2-ui/interface/Theme.cpp b/src/openrct2-ui/interface/Theme.cpp
index f0a9905b86..65ef37be3b 100644
--- a/src/openrct2-ui/interface/Theme.cpp
+++ b/src/openrct2-ui/interface/Theme.cpp
@@ -174,6 +174,7 @@ static constexpr const WindowThemeDesc WindowThemeDescriptors[] =
{ THEME_WC(WC_TITLE_EDITOR), STR_TITLE_EDITOR_TITLE, COLOURS_3(COLOUR_GREY, COLOUR_OLIVE_GREEN, COLOUR_OLIVE_GREEN ) },
{ THEME_WC(WC_TILE_INSPECTOR), STR_TILE_INSPECTOR_TITLE, COLOURS_2(COLOUR_LIGHT_BLUE, COLOUR_LIGHT_BLUE ) },
{ THEME_WC(WC_VIEW_CLIPPING), STR_VIEW_CLIPPING_TITLE, COLOURS_1(COLOUR_DARK_GREEN ) },
+ { THEME_WC(WC_PATROL_AREA), STR_SET_PATROL_AREA, COLOURS_3(COLOUR_LIGHT_PURPLE, COLOUR_LIGHT_PURPLE, COLOUR_LIGHT_PURPLE ) },
{ THEME_WC(WC_ABOUT), STR_ABOUT, COLOURS_2(COLOUR_GREY, COLOUR_LIGHT_BLUE ) },
{ THEME_WC(WC_CHANGELOG), STR_CHANGELOG_TITLE, COLOURS_2(COLOUR_LIGHT_BLUE, COLOUR_LIGHT_BLUE ) },
{ THEME_WC(WC_MULTIPLAYER), STR_MULTIPLAYER, COLOURS_3(COLOUR_LIGHT_BLUE, COLOUR_LIGHT_BLUE, COLOUR_LIGHT_BLUE ) },
diff --git a/src/openrct2-ui/libopenrct2ui.vcxproj b/src/openrct2-ui/libopenrct2ui.vcxproj
index b8a3649b3c..1103d25e87 100644
--- a/src/openrct2-ui/libopenrct2ui.vcxproj
+++ b/src/openrct2-ui/libopenrct2ui.vcxproj
@@ -156,6 +156,7 @@
+
diff --git a/src/openrct2-ui/windows/PatrolArea.cpp b/src/openrct2-ui/windows/PatrolArea.cpp
new file mode 100644
index 0000000000..fe89e273ad
--- /dev/null
+++ b/src/openrct2-ui/windows/PatrolArea.cpp
@@ -0,0 +1,305 @@
+/*****************************************************************************
+ * Copyright (c) 2014-2020 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.
+ *****************************************************************************/
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+static constexpr const rct_string_id WINDOW_TITLE = STR_SET_PATROL_AREA;
+static constexpr const int32_t WH = 54;
+static constexpr const int32_t WW = 104;
+
+enum WindowPatrolAreaWidgetIdx
+{
+ WIDX_BACKGROUND,
+ WIDX_TITLE,
+ WIDX_CLOSE,
+ WIDX_PREVIEW,
+ WIDX_DECREMENT,
+ WIDX_INCREMENT,
+};
+
+// clang-format off
+static rct_widget PatrolAreaWidgets[] = {
+ WINDOW_SHIM(WINDOW_TITLE, WW, WH),
+ MakeWidget ({27, 17}, {44, 32}, WindowWidgetType::ImgBtn, WindowColour::Primary , SPR_LAND_TOOL_SIZE_0 ), // preview box
+ MakeRemapWidget({28, 18}, {16, 16}, WindowWidgetType::TrnBtn, WindowColour::Tertiary, SPR_LAND_TOOL_DECREASE, STR_ADJUST_SMALLER_PATROL_AREA_TIP), // decrement size
+ MakeRemapWidget({54, 32}, {16, 16}, WindowWidgetType::TrnBtn, WindowColour::Tertiary, SPR_LAND_TOOL_INCREASE, STR_ADJUST_LARGER_PATROL_AREA_TIP ), // increment size
+ WIDGETS_END,
+};
+// clang-format on
+
+class PatrolAreaWindow final : public Window
+{
+public:
+ void OnOpen() override
+ {
+ widgets = PatrolAreaWidgets;
+ hold_down_widgets = (1ULL << WIDX_INCREMENT) | (1ULL << WIDX_DECREMENT);
+ WindowInitScrollWidgets(this);
+ window_push_others_below(this);
+ gLandToolSize = 4;
+ }
+
+ void OnClose() override
+ {
+ // If the tool wasn't changed, turn tool off
+ if (PatrolAreaToolIsActive())
+ tool_cancel();
+ }
+
+ void OnMouseUp(rct_widgetindex widgetIndex) override
+ {
+ switch (widgetIndex)
+ {
+ case WIDX_CLOSE:
+ Close();
+ break;
+ case WIDX_PREVIEW:
+ InputSize();
+ break;
+ }
+ }
+
+ void OnMouseDown(rct_widgetindex widgetIndex) override
+ {
+ switch (widgetIndex)
+ {
+ case WIDX_DECREMENT:
+ gLandToolSize = std::max(MINIMUM_TOOL_SIZE, gLandToolSize - 1);
+ Invalidate();
+ break;
+ case WIDX_INCREMENT:
+ gLandToolSize = std::min(MAXIMUM_TOOL_SIZE, gLandToolSize + 1);
+ Invalidate();
+ break;
+ }
+ }
+
+ void OnTextInput(rct_widgetindex widgetIndex, std::string_view text) override
+ {
+ if (text.empty())
+ return;
+
+ if (widgetIndex != WIDX_PREVIEW)
+ return;
+
+ const auto res = String::Parse(text);
+ if (res.has_value())
+ {
+ int32_t size;
+ size = res.value();
+ size = std::max(MINIMUM_TOOL_SIZE, size);
+ size = std::min(MAXIMUM_TOOL_SIZE, size);
+ gLandToolSize = size;
+ Invalidate();
+ }
+ }
+
+ void OnUpdate() override
+ {
+ // Close window if another tool is open or staff window gets closed
+ if (!PatrolAreaToolIsActive() || !IsStaffWindowOpen())
+ {
+ Close();
+ }
+ }
+
+ void OnPrepareDraw() override
+ {
+ SetWidgetPressed(WIDX_PREVIEW, true);
+ PatrolAreaWidgets[WIDX_PREVIEW].image = LandTool::SizeToSpriteIndex(gLandToolSize);
+ }
+
+ void OnDraw(rct_drawpixelinfo& dpi) override
+ {
+ DrawWidgets(dpi);
+
+ // Draw number for tool sizes bigger than 7
+ if (gLandToolSize > MAX_TOOL_SIZE_WITH_SPRITE)
+ {
+ auto screenCoords = ScreenCoordsXY{ windowPos.x + PatrolAreaWidgets[WIDX_PREVIEW].midX(),
+ windowPos.y + PatrolAreaWidgets[WIDX_PREVIEW].midY() };
+ auto ft = Formatter();
+ ft.Add(gLandToolSize);
+ DrawTextBasic(&dpi, screenCoords - ScreenCoordsXY{ 0, 2 }, STR_LAND_TOOL_SIZE_VALUE, ft, { TextAlignment::CENTRE });
+ }
+ }
+
+ void OnToolUpdate(rct_widgetindex widgetIndex, const ScreenCoordsXY& screenCoords) override
+ {
+ auto mapTile = GetBestCoordsFromPos(screenCoords);
+ if (!mapTile)
+ return;
+
+ auto stateChanged = false;
+ if (!(gMapSelectFlags & MAP_SELECT_FLAG_ENABLE))
+ stateChanged = true;
+
+ if (gMapSelectType != MAP_SELECT_TYPE_FULL)
+ stateChanged = true;
+
+ auto toolSize = std::max(1, gLandToolSize);
+ auto toolLength = (toolSize - 1) * 32;
+
+ // Move to tool bottom left
+ mapTile->x -= (toolSize - 1) * 16;
+ mapTile->y -= (toolSize - 1) * 16;
+ mapTile = mapTile->ToTileStart();
+ auto posA = *mapTile;
+ mapTile->x += toolLength;
+ mapTile->y += toolLength;
+ auto posB = *mapTile;
+ if (gMapSelectPositionA != posA || gMapSelectPositionB != posB)
+ stateChanged = true;
+
+ if (stateChanged)
+ {
+ // Invalidate previous area
+ map_invalidate_selection_rect();
+
+ // Update and invalidate new area
+ gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE;
+ gMapSelectType = MAP_SELECT_TYPE_FULL;
+ gMapSelectPositionA = posA;
+ gMapSelectPositionB = posB;
+ map_invalidate_selection_rect();
+ }
+ }
+
+ void OnToolAbort(rct_widgetindex widgetIndex) override
+ {
+ hide_gridlines();
+ ClearPatrolAreaToRender();
+ gfx_invalidate_screen();
+ }
+
+ void OnToolDown(rct_widgetindex widgetIndex, const ScreenCoordsXY& screenCoords) override
+ {
+ auto mapTile = GetBestCoordsFromPos(screenCoords);
+ if (mapTile)
+ {
+ auto staff = GetEntity(_staffId);
+ if (staff != nullptr)
+ {
+ _mode = staff->IsPatrolAreaSet(*mapTile) ? StaffSetPatrolAreaMode::Unset : StaffSetPatrolAreaMode::Set;
+ }
+ }
+
+ OnToolDrag(widgetIndex, screenCoords);
+ }
+
+ void OnToolDrag(rct_widgetindex widgetIndex, const ScreenCoordsXY& screenCoords) override
+ {
+ auto staff = GetEntity(_staffId);
+ if (staff != nullptr)
+ {
+ MapRange range(gMapSelectPositionA, gMapSelectPositionB);
+ auto staffSetPatrolAreaAction = StaffSetPatrolAreaAction(_staffId, range, _mode);
+ GameActions::Execute(&staffSetPatrolAreaAction);
+ }
+ }
+
+ EntityId GetStaffId() const
+ {
+ return _staffId;
+ }
+
+ void SetStaffId(EntityId staffId)
+ {
+ _staffId = staffId;
+ EnableTool();
+ }
+
+private:
+ EntityId _staffId;
+ StaffSetPatrolAreaMode _mode;
+
+ void EnableTool()
+ {
+ show_gridlines();
+ if (!tool_set(this, 0, Tool::WalkDown))
+ {
+ input_set_flag(INPUT_FLAG_6, true);
+ show_gridlines();
+ SetPatrolAreaToRender(_staffId);
+ gfx_invalidate_screen();
+ }
+ }
+
+ void InputSize()
+ {
+ Formatter ft;
+ ft.Add(MINIMUM_TOOL_SIZE);
+ ft.Add(MAXIMUM_TOOL_SIZE);
+ WindowTextInputOpen(this, WIDX_PREVIEW, STR_SELECTION_SIZE, STR_ENTER_SELECTION_SIZE, ft, STR_NONE, STR_NONE, 3);
+ }
+
+ bool PatrolAreaToolIsActive()
+ {
+ if (!(input_test_flag(INPUT_FLAG_TOOL_ACTIVE)))
+ return false;
+ if (gCurrentToolWidget.window_classification != WC_PATROL_AREA)
+ return false;
+ return true;
+ }
+
+ bool IsStaffWindowOpen()
+ {
+ // If staff window for this patrol area was closed, tool is no longer active
+ auto staffWindow = window_find_by_number(WC_PEEP, _staffId);
+ return staffWindow != nullptr;
+ }
+
+ std::optional GetBestCoordsFromPos(const ScreenCoordsXY& pos)
+ {
+ auto coords = footpath_get_coordinates_from_pos(pos, nullptr, nullptr);
+ return coords.IsNull() ? std::nullopt : std::make_optional(coords);
+ }
+};
+
+rct_window* WindowPatrolAreaOpen(EntityId staffId)
+{
+ auto current = reinterpret_cast(window_find_by_class(WC_PATROL_AREA));
+ if (current != nullptr)
+ {
+ if (current->GetStaffId() == staffId)
+ {
+ return window_bring_to_front(current);
+ }
+ current->Close();
+ }
+
+ auto w = WindowFocusOrCreate(WC_PATROL_AREA, ScreenCoordsXY(context_get_width() - WW, 29), WW, WH, 0);
+ if (w != nullptr)
+ {
+ w->SetStaffId(staffId);
+ }
+ return w;
+}
+
+EntityId WindowPatrolAreaGetCurrentStaffId()
+{
+ auto current = reinterpret_cast(window_find_by_class(WC_PATROL_AREA));
+ return current != nullptr ? current->GetStaffId() : EntityId();
+}
diff --git a/src/openrct2-ui/windows/Staff.cpp b/src/openrct2-ui/windows/Staff.cpp
index ecf681b5e9..5172d72cc9 100644
--- a/src/openrct2-ui/windows/Staff.cpp
+++ b/src/openrct2-ui/windows/Staff.cpp
@@ -135,8 +135,6 @@ static void WindowStaffOverviewPaint(rct_window* w, rct_drawpixelinfo* dpi);
static void WindowStaffOverviewTabPaint(rct_window* w, rct_drawpixelinfo* dpi);
static void WindowStaffOverviewToolUpdate(rct_window* w, rct_widgetindex widgetIndex, const ScreenCoordsXY& screenCoords);
static void WindowStaffOverviewToolDown(rct_window* w, rct_widgetindex widgetIndex, const ScreenCoordsXY& screenCoords);
-static void WindowStaffOverviewToolDrag(rct_window* w, rct_widgetindex widgetIndex, const ScreenCoordsXY& screenCoords);
-static void WindowStaffOverviewToolUp(rct_window* w, rct_widgetindex widgetIndex, const ScreenCoordsXY& screenCoords);
static void WindowStaffOverviewToolAbort(rct_window* w, rct_widgetindex widgetIndex);
static void WindowStaffOverviewTextInput(rct_window* w, rct_widgetindex widgetIndex, char* text);
static void WindowStaffOverviewViewportRotate(rct_window* w);
@@ -170,8 +168,6 @@ static rct_window_event_list window_staff_overview_events([](auto& events) {
events.update = &WindowStaffOverviewUpdate;
events.tool_update = &WindowStaffOverviewToolUpdate;
events.tool_down = &WindowStaffOverviewToolDown;
- events.tool_drag = &WindowStaffOverviewToolDrag;
- events.tool_up = &WindowStaffOverviewToolUp;
events.tool_abort = &WindowStaffOverviewToolAbort;
events.text_input = &WindowStaffOverviewTextInput;
events.viewport_rotate = &WindowStaffOverviewViewportRotate;
@@ -209,15 +205,6 @@ static rct_window_event_list* window_staff_page_events[] = {
static EntertainerCostume _availableCostumes[static_cast(EntertainerCostume::Count)];
-enum class PatrolAreaValue
-{
- UNSET = 0,
- SET = 1,
- NONE = -1,
-};
-
-static PatrolAreaValue _staffPatrolAreaPaintValue = PatrolAreaValue::NONE;
-
static Staff* GetStaff(rct_window* w)
{
auto staff = GetEntity(EntityId::FromUnderlying(w->number));
@@ -542,17 +529,22 @@ void WindowStaffOverviewDropdown(rct_window* w, rct_widgetindex widgetIndex, int
return;
}
+ window_close_by_class(WC_PATROL_AREA);
+
auto staffSetPatrolAreaAction = StaffSetPatrolAreaAction(
peep->sprite_index, {}, StaffSetPatrolAreaMode::ClearAll);
GameActions::Execute(&staffSetPatrolAreaAction);
}
else
{
- if (!tool_set(w, widgetIndex, Tool::WalkDown))
+ auto staffId = EntityId::FromUnderlying(w->number);
+ if (WindowPatrolAreaGetCurrentStaffId() == staffId)
{
- show_gridlines();
- SetPatrolAreaToRender(EntityId::FromUnderlying(w->number));
- gfx_invalidate_screen();
+ window_close_by_class(WC_PATROL_AREA);
+ }
+ else
+ {
+ WindowPatrolAreaOpen(staffId);
}
}
break;
@@ -895,6 +887,7 @@ void WindowStaffOverviewInvalidate(rct_window* w)
window_staff_overview_widgets[WIDX_PICKUP].left = w->width - 25;
window_staff_overview_widgets[WIDX_PICKUP].right = w->width - 2;
+ WidgetSetPressed(w, WIDX_PATROL, WindowPatrolAreaGetCurrentStaffId() == peep->sprite_index);
window_staff_overview_widgets[WIDX_PATROL].left = w->width - 25;
window_staff_overview_widgets[WIDX_PATROL].right = w->width - 2;
@@ -1174,96 +1167,26 @@ void WindowStaffOverviewToolUpdate(rct_window* w, rct_widgetindex widgetIndex, c
*/
void WindowStaffOverviewToolDown(rct_window* w, rct_widgetindex widgetIndex, const ScreenCoordsXY& screenCoords)
{
+ if (widgetIndex != WIDX_PICKUP)
+ return;
+
const auto staffEntityId = EntityId::FromUnderlying(w->number);
-
- if (widgetIndex == WIDX_PICKUP)
- {
- TileElement* tileElement;
- auto destCoords = footpath_get_coordinates_from_pos({ screenCoords.x, screenCoords.y + 16 }, nullptr, &tileElement);
-
- if (destCoords.IsNull())
- return;
-
- PeepPickupAction pickupAction{
- PeepPickupType::Place, staffEntityId, { destCoords, tileElement->GetBaseZ() }, network_get_current_player_id()
- };
- pickupAction.SetCallback([](const GameAction* ga, const GameActions::Result* result) {
- if (result->Error != GameActions::Status::Ok)
- return;
- tool_cancel();
- gPickupPeepImage = ImageId();
- });
- GameActions::Execute(&pickupAction);
- }
- else if (widgetIndex == WIDX_PATROL)
- {
- auto destCoords = footpath_get_coordinates_from_pos(screenCoords, nullptr, nullptr);
-
- if (destCoords.IsNull())
- return;
-
- auto staff = TryGetEntity(staffEntityId);
- if (staff == nullptr)
- return;
-
- if (staff->IsPatrolAreaSet(destCoords))
- {
- _staffPatrolAreaPaintValue = PatrolAreaValue::UNSET;
- }
- else
- {
- _staffPatrolAreaPaintValue = PatrolAreaValue::SET;
- }
- auto staffSetPatrolAreaAction = StaffSetPatrolAreaAction(
- staffEntityId, destCoords,
- _staffPatrolAreaPaintValue == PatrolAreaValue::SET ? StaffSetPatrolAreaMode::Set : StaffSetPatrolAreaMode::Unset);
- GameActions::Execute(&staffSetPatrolAreaAction);
- }
-}
-
-void WindowStaffOverviewToolDrag(rct_window* w, rct_widgetindex widgetIndex, const ScreenCoordsXY& screenCoords)
-{
- if (widgetIndex != WIDX_PATROL)
- return;
-
- if (network_get_mode() != NETWORK_MODE_NONE)
- return;
-
- // This works only for singleplayer if the game_do_command can not be prevented
- // to send packets more often than patrol area is updated.
-
- if (_staffPatrolAreaPaintValue == PatrolAreaValue::NONE)
- return; // Do nothing if we do not have a paintvalue(this should never happen)
-
- auto destCoords = footpath_get_coordinates_from_pos(screenCoords, nullptr, nullptr);
+ TileElement* tileElement;
+ auto destCoords = footpath_get_coordinates_from_pos({ screenCoords.x, screenCoords.y + 16 }, nullptr, &tileElement);
if (destCoords.IsNull())
return;
- const auto staffEntityId = EntityId::FromUnderlying(w->number);
-
- auto* staff = TryGetEntity(staffEntityId);
- if (staff == nullptr)
- return;
-
- bool patrolAreaValue = staff->IsPatrolAreaSet(destCoords);
- if (_staffPatrolAreaPaintValue == PatrolAreaValue::SET && patrolAreaValue)
- return; // Since area is already the value we want, skip...
- if (_staffPatrolAreaPaintValue == PatrolAreaValue::UNSET && !patrolAreaValue)
- return; // Since area is already the value we want, skip...
-
- auto staffSetPatrolAreaAction = StaffSetPatrolAreaAction(
- staffEntityId, destCoords,
- _staffPatrolAreaPaintValue == PatrolAreaValue::SET ? StaffSetPatrolAreaMode::Set : StaffSetPatrolAreaMode::Unset);
- GameActions::Execute(&staffSetPatrolAreaAction);
-}
-
-void WindowStaffOverviewToolUp(rct_window* w, rct_widgetindex widgetIndex, const ScreenCoordsXY& screenCoords)
-{
- if (widgetIndex != WIDX_PATROL)
- return;
-
- _staffPatrolAreaPaintValue = PatrolAreaValue::NONE;
+ PeepPickupAction pickupAction{
+ PeepPickupType::Place, staffEntityId, { destCoords, tileElement->GetBaseZ() }, network_get_current_player_id()
+ };
+ pickupAction.SetCallback([](const GameAction* ga, const GameActions::Result* result) {
+ if (result->Error != GameActions::Status::Ok)
+ return;
+ tool_cancel();
+ gPickupPeepImage = ImageId();
+ });
+ GameActions::Execute(&pickupAction);
}
/**
@@ -1272,20 +1195,14 @@ void WindowStaffOverviewToolUp(rct_window* w, rct_widgetindex widgetIndex, const
*/
void WindowStaffOverviewToolAbort(rct_window* w, rct_widgetindex widgetIndex)
{
- if (widgetIndex == WIDX_PICKUP)
- {
- PeepPickupAction pickupAction{ PeepPickupType::Cancel,
- EntityId::FromUnderlying(w->number),
- { w->picked_peep_old_x, 0, 0 },
- network_get_current_player_id() };
- GameActions::Execute(&pickupAction);
- }
- else if (widgetIndex == WIDX_PATROL)
- {
- hide_gridlines();
- ClearPatrolAreaToRender();
- gfx_invalidate_screen();
- }
+ if (widgetIndex != WIDX_PICKUP)
+ return;
+
+ PeepPickupAction pickupAction{ PeepPickupType::Cancel,
+ EntityId::FromUnderlying(w->number),
+ { w->picked_peep_old_x, 0, 0 },
+ network_get_current_player_id() };
+ GameActions::Execute(&pickupAction);
}
/* rct2: 0x6BDFED */
diff --git a/src/openrct2-ui/windows/Themes.cpp b/src/openrct2-ui/windows/Themes.cpp
index 6ef1cb0069..5af77f7cbd 100644
--- a/src/openrct2-ui/windows/Themes.cpp
+++ b/src/openrct2-ui/windows/Themes.cpp
@@ -195,6 +195,7 @@ static rct_windowclass window_themes_tab_3_classes[] = {
WC_TRACK_DESIGN_PLACE,
WC_CONSTRUCT_RIDE,
WC_TRACK_DESIGN_LIST,
+ WC_PATROL_AREA,
};
static rct_windowclass window_themes_tab_4_classes[] = {
diff --git a/src/openrct2-ui/windows/Window.h b/src/openrct2-ui/windows/Window.h
index 362e914a10..ae814db5d4 100644
--- a/src/openrct2-ui/windows/Window.h
+++ b/src/openrct2-ui/windows/Window.h
@@ -10,6 +10,7 @@
#pragma once
#include
+#include
#include
#include
#include
@@ -200,6 +201,8 @@ void WindowTooltipOpen(rct_window* widgetWindow, rct_widgetindex widgetIndex, co
void WindowTooltipClose();
rct_window* WindowSceneryScatterOpen();
+rct_window* WindowPatrolAreaOpen(EntityId staffId);
+EntityId WindowPatrolAreaGetCurrentStaffId();
// clang-format off
#define WINDOW_SHIM_RAW(TITLE, WIDTH, HEIGHT, CLOSE_STR) \
diff --git a/src/openrct2/actions/GameAction.h b/src/openrct2/actions/GameAction.h
index 7e5e89eb0a..c0a6fe2d12 100644
--- a/src/openrct2/actions/GameAction.h
+++ b/src/openrct2/actions/GameAction.h
@@ -79,6 +79,14 @@ public:
Visit("direction", param.direction);
}
+ void Visit(MapRange& param)
+ {
+ Visit("x1", param.Point1.x);
+ Visit("y1", param.Point1.y);
+ Visit("x2", param.Point2.x);
+ Visit("y2", param.Point2.y);
+ }
+
template void Visit(std::string_view name, T& param)
{
static_assert(std::is_arithmetic_v || std::is_enum_v, "Not an arithmetic type");
diff --git a/src/openrct2/actions/StaffSetPatrolAreaAction.cpp b/src/openrct2/actions/StaffSetPatrolAreaAction.cpp
index 2c528ed40e..601292adb2 100644
--- a/src/openrct2/actions/StaffSetPatrolAreaAction.cpp
+++ b/src/openrct2/actions/StaffSetPatrolAreaAction.cpp
@@ -15,13 +15,20 @@
#include "../entity/Staff.h"
#include "../interface/Window.h"
-StaffSetPatrolAreaAction::StaffSetPatrolAreaAction(EntityId spriteId, const CoordsXY& loc, const StaffSetPatrolAreaMode mode)
+StaffSetPatrolAreaAction::StaffSetPatrolAreaAction(EntityId spriteId, const MapRange& range, const StaffSetPatrolAreaMode mode)
: _spriteId(spriteId)
- , _loc(loc)
+ , _range(range)
, _mode(mode)
{
}
+void StaffSetPatrolAreaAction::AcceptParameters(GameActionParameterVisitor& visitor)
+{
+ visitor.Visit("id", _spriteId);
+ visitor.Visit(_range);
+ visitor.Visit("mode", _mode);
+}
+
uint16_t StaffSetPatrolAreaAction::GetActionFlags() const
{
return GameAction::GetActionFlags() | GameActions::Flags::AllowWhilePaused;
@@ -30,7 +37,7 @@ uint16_t StaffSetPatrolAreaAction::GetActionFlags() const
void StaffSetPatrolAreaAction::Serialise(DataSerialiser& stream)
{
GameAction::Serialise(stream);
- stream << DS_TAG(_spriteId) << DS_TAG(_loc) << DS_TAG(_mode);
+ stream << DS_TAG(_spriteId) << DS_TAG(_range) << DS_TAG(_mode);
}
GameActions::Result StaffSetPatrolAreaAction::Query() const
@@ -41,11 +48,6 @@ GameActions::Result StaffSetPatrolAreaAction::Query() const
return GameActions::Result(GameActions::Status::InvalidParameters, STR_NONE, STR_NONE);
}
- if (!LocationValid(_loc))
- {
- return GameActions::Result(GameActions::Status::InvalidParameters, STR_NONE, STR_NONE);
- }
-
auto staff = TryGetEntity(_spriteId);
if (staff == nullptr)
{
@@ -56,17 +58,9 @@ GameActions::Result StaffSetPatrolAreaAction::Query() const
return GameActions::Result();
}
-static void InvalidatePatrolTile(const CoordsXY& loc)
+static void InvalidatePatrolTiles(const MapRange& range)
{
- // Align the location to the top left of the patrol square
- const auto alignedLoc = CoordsXY{ loc.x & 0x1F80, loc.y & 0x1F80 };
- for (int32_t y = 0; y < 4 * COORDS_XY_STEP; y += COORDS_XY_STEP)
- {
- for (int32_t x = 0; x < 4 * COORDS_XY_STEP; x += COORDS_XY_STEP)
- {
- map_invalidate_tile_full(alignedLoc + CoordsXY{ x, y });
- }
- }
+ map_invalidate_region(range.Point1, range.Point2);
}
GameActions::Result StaffSetPatrolAreaAction::Execute() const
@@ -81,16 +75,16 @@ GameActions::Result StaffSetPatrolAreaAction::Execute() const
switch (_mode)
{
case StaffSetPatrolAreaMode::Set:
- staff->SetPatrolArea(_loc, true);
- InvalidatePatrolTile(_loc);
+ staff->SetPatrolArea(_range, true);
+ InvalidatePatrolTiles(_range);
break;
case StaffSetPatrolAreaMode::Unset:
- staff->SetPatrolArea(_loc, false);
+ staff->SetPatrolArea(_range, false);
if (!staff->HasPatrolArea())
{
staff->ClearPatrolArea();
}
- InvalidatePatrolTile(_loc);
+ InvalidatePatrolTiles(_range);
break;
case StaffSetPatrolAreaMode::ClearAll:
staff->ClearPatrolArea();
diff --git a/src/openrct2/actions/StaffSetPatrolAreaAction.h b/src/openrct2/actions/StaffSetPatrolAreaAction.h
index 939f608a73..cdfc10b533 100644
--- a/src/openrct2/actions/StaffSetPatrolAreaAction.h
+++ b/src/openrct2/actions/StaffSetPatrolAreaAction.h
@@ -22,15 +22,16 @@ class StaffSetPatrolAreaAction final : public GameActionBase