1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2026-01-24 07:14:31 +01:00

Add patrol area tool window

This commit is contained in:
Ted John
2022-03-11 20:26:29 +00:00
parent 97dfe3cb73
commit 38d36acef3
12 changed files with 377 additions and 140 deletions

View File

@@ -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 #

View File

@@ -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 ) },

View File

@@ -156,6 +156,7 @@
<ClCompile Include="windows\ObjectLoadError.cpp" />
<ClCompile Include="windows\Options.cpp" />
<ClCompile Include="windows\Park.cpp" />
<ClCompile Include="windows\PatrolArea.cpp" />
<ClCompile Include="windows\Player.cpp" />
<ClCompile Include="windows\RefurbishRidePrompt.cpp" />
<ClCompile Include="windows\Research.cpp" />

View File

@@ -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 <algorithm>
#include <openrct2-ui/interface/LandTool.h>
#include <openrct2-ui/interface/Viewport.h>
#include <openrct2-ui/interface/Widget.h>
#include <openrct2-ui/windows/Window.h>
#include <openrct2/Context.h>
#include <openrct2/Game.h>
#include <openrct2/Input.h>
#include <openrct2/actions/StaffSetPatrolAreaAction.h>
#include <openrct2/core/String.hpp>
#include <openrct2/drawing/Drawing.h>
#include <openrct2/entity/EntityRegistry.h>
#include <openrct2/entity/PatrolArea.h>
#include <openrct2/entity/Staff.h>
#include <openrct2/localisation/Formatter.h>
#include <openrct2/localisation/Localisation.h>
#include <openrct2/world/Park.h>
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<int32_t>(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<uint16_t>(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<uint16_t>(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<Staff>(_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<Staff>(_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<int16_t>(MINIMUM_TOOL_SIZE);
ft.Add<int16_t>(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<CoordsXY> 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<PatrolAreaWindow*>(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<PatrolAreaWindow>(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<PatrolAreaWindow*>(window_find_by_class(WC_PATROL_AREA));
return current != nullptr ? current->GetStaffId() : EntityId();
}

View File

@@ -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<uint8_t>(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<Staff>(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<Staff>(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<Staff>(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 */

View File

@@ -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[] = {

View File

@@ -10,6 +10,7 @@
#pragma once
#include <openrct2-ui/interface/Window.h>
#include <openrct2/Identifiers.h>
#include <openrct2/common.h>
#include <openrct2/ride/Ride.h>
#include <openrct2/windows/tile_inspector.h>
@@ -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) \

View File

@@ -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<typename T> void Visit(std::string_view name, T& param)
{
static_assert(std::is_arithmetic_v<T> || std::is_enum_v<T>, "Not an arithmetic type");

View File

@@ -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<Staff>(_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();

View File

@@ -22,15 +22,16 @@ class StaffSetPatrolAreaAction final : public GameActionBase<GameCommand::SetSta
{
private:
EntityId _spriteId{ EntityId::GetNull() };
CoordsXY _loc;
MapRange _range;
StaffSetPatrolAreaMode _mode;
public:
StaffSetPatrolAreaAction() = default;
StaffSetPatrolAreaAction(EntityId spriteId, const CoordsXY& loc, const StaffSetPatrolAreaMode mode);
StaffSetPatrolAreaAction(EntityId spriteId, const MapRange& range, const StaffSetPatrolAreaMode mode);
uint16_t GetActionFlags() const override;
void AcceptParameters(GameActionParameterVisitor& visitor) override;
void Serialise(DataSerialiser& stream) override;
GameActions::Result Query() const override;
GameActions::Result Execute() const override;

View File

@@ -462,6 +462,7 @@ enum
WC_DEBUG_PAINT = 130,
WC_VIEW_CLIPPING = 131,
WC_OBJECT_LOAD_ERROR = 132,
WC_PATROL_AREA = 133,
// Only used for colour schemes
WC_STAFF = 220,

View File

@@ -3925,6 +3925,9 @@ enum : uint16_t
STR_NAUSEA_LABEL = 6467,
STR_RATING_UKNOWN_LABEL = 6468,
STR_ADJUST_SMALLER_PATROL_AREA_TIP = 6469,
STR_ADJUST_LARGER_PATROL_AREA_TIP = 6470,
// Have to include resource strings (from scenarios and objects) for the time being now that language is partially working
/* MAX_STR_COUNT = 32768 */ // MAX_STR_COUNT - upper limit for number of strings, not the current count strings
};