mirror of
https://github.com/OpenRCT2/OpenRCT2
synced 2025-12-16 04:22:43 +01:00
Add footpath dragging tool
This commit is contained in:
committed by
GitHub
parent
a2cc3c20c6
commit
ff580112c8
@@ -3843,3 +3843,4 @@ STR_7001 :Ride name
|
|||||||
STR_7002 :{STRINGID} {STRINGID}
|
STR_7002 :{STRINGID} {STRINGID}
|
||||||
STR_7003 :Audio file ‘{STRING}’ is truncated. Expected sample {INT32}, but only {INT32} are available. Consider reinstalling RCT2.
|
STR_7003 :Audio file ‘{STRING}’ is truncated. Expected sample {INT32}, but only {INT32} are available. Consider reinstalling RCT2.
|
||||||
STR_7004 :Force Redraw
|
STR_7004 :Force Redraw
|
||||||
|
STR_7005 :Drag an area of footpath
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
0.4.28 (in development)
|
0.4.28 (in development)
|
||||||
------------------------------------------------------------------------
|
------------------------------------------------------------------------
|
||||||
|
- Feature: [#25286] Footpath area dragging tool.
|
||||||
- Change: [#25089] Peep actions and animations that cause them to stop moving no longer trigger when they are on a level crossing.
|
- Change: [#25089] Peep actions and animations that cause them to stop moving no longer trigger when they are on a level crossing.
|
||||||
- Change: [#25337] Placing track designs with scenery that is obstructed no longer disables all of the scenery.
|
- Change: [#25337] Placing track designs with scenery that is obstructed no longer disables all of the scenery.
|
||||||
- Fix: [#20486] Multiplayer desync when placing track designs without any scenery.
|
- Fix: [#20486] Multiplayer desync when placing track designs without any scenery.
|
||||||
|
|||||||
@@ -838,5 +838,10 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "icons/rotate_view_anti-clockwise.png"
|
"path": "icons/rotate_view_anti-clockwise.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "tool/path_drag_tool.png",
|
||||||
|
"x": 1,
|
||||||
|
"y": 2
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
BIN
resources/g2/tool/path_drag_tool.png
Normal file
BIN
resources/g2/tool/path_drag_tool.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 481 B |
@@ -703,6 +703,7 @@ namespace OpenRCT2
|
|||||||
STR_CONSTRUCT_THE_SELECTED_FOOTPATH_SECTION_TIP = 1189,
|
STR_CONSTRUCT_THE_SELECTED_FOOTPATH_SECTION_TIP = 1189,
|
||||||
STR_DIRECTION = 1183,
|
STR_DIRECTION = 1183,
|
||||||
STR_DIRECTION_TIP = 1185,
|
STR_DIRECTION_TIP = 1185,
|
||||||
|
STR_DRAG_AREA_OF_FOOTPATH_TIP = 7005,
|
||||||
STR_FOOTPATHS = 1181,
|
STR_FOOTPATHS = 1181,
|
||||||
STR_FOOTPATH_TIP = 1424,
|
STR_FOOTPATH_TIP = 1424,
|
||||||
STR_LEVEL_TIP = 1187,
|
STR_LEVEL_TIP = 1187,
|
||||||
|
|||||||
@@ -50,13 +50,25 @@ using namespace OpenRCT2::Numerics;
|
|||||||
|
|
||||||
namespace OpenRCT2::Ui::Windows
|
namespace OpenRCT2::Ui::Windows
|
||||||
{
|
{
|
||||||
|
struct ProvisionalTile
|
||||||
|
{
|
||||||
|
CoordsXYZ position;
|
||||||
|
FootpathSlope slope;
|
||||||
|
|
||||||
|
constexpr bool operator==(const ProvisionalTile& rhs) const
|
||||||
|
{
|
||||||
|
return position == rhs.position && slope == rhs.slope;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
static money64 FootpathProvisionalSet(
|
static money64 FootpathProvisionalSet(
|
||||||
ObjectEntryIndex type, ObjectEntryIndex railingsType, const CoordsXYZ& footpathLoc, FootpathSlope slope,
|
ObjectEntryIndex type, ObjectEntryIndex railingsType, const CoordsXY& footpathLocA, const CoordsXY& footpathLocB,
|
||||||
PathConstructFlags constructFlags);
|
int32_t startZ, const std::span<const ProvisionalTile> tiles, PathConstructFlags constructFlags);
|
||||||
|
|
||||||
enum class PathConstructionMode : uint8_t
|
enum class PathConstructionMode : uint8_t
|
||||||
{
|
{
|
||||||
land,
|
onLand,
|
||||||
|
dragArea,
|
||||||
/**
|
/**
|
||||||
* When picking a location to start the bridge or tunnel
|
* When picking a location to start the bridge or tunnel
|
||||||
*/
|
*/
|
||||||
@@ -85,15 +97,21 @@ namespace OpenRCT2::Ui::Windows
|
|||||||
};
|
};
|
||||||
|
|
||||||
using ProvisionalPathFlags = FlagHolder<uint8_t, ProvisionalPathFlag>;
|
using ProvisionalPathFlags = FlagHolder<uint8_t, ProvisionalPathFlag>;
|
||||||
|
|
||||||
struct ProvisionalFootpath
|
struct ProvisionalFootpath
|
||||||
{
|
{
|
||||||
ObjectEntryIndex type;
|
ObjectEntryIndex type{};
|
||||||
CoordsXYZ position;
|
CoordsXY positionA{};
|
||||||
FootpathSlope slope;
|
CoordsXY positionB{};
|
||||||
ProvisionalPathFlags flags;
|
/**
|
||||||
ObjectEntryIndex surfaceIndex;
|
* Z of the first tile. Used for checking if the provisional path needs updating when in dragArea mode.
|
||||||
ObjectEntryIndex railingsIndex;
|
*/
|
||||||
PathConstructFlags constructFlags;
|
int32_t startZ{};
|
||||||
|
ProvisionalPathFlags flags{};
|
||||||
|
ObjectEntryIndex surfaceIndex{};
|
||||||
|
ObjectEntryIndex railingsIndex{};
|
||||||
|
PathConstructFlags constructFlags{};
|
||||||
|
std::vector<ProvisionalTile> tiles{};
|
||||||
};
|
};
|
||||||
|
|
||||||
static ProvisionalFootpath _provisionalFootpath;
|
static ProvisionalFootpath _provisionalFootpath;
|
||||||
@@ -103,7 +121,7 @@ namespace OpenRCT2::Ui::Windows
|
|||||||
#pragma region Measurements
|
#pragma region Measurements
|
||||||
|
|
||||||
static constexpr StringId kWindowTitle = STR_FOOTPATHS;
|
static constexpr StringId kWindowTitle = STR_FOOTPATHS;
|
||||||
static constexpr ScreenSize kWindowSize = { 106, 421 };
|
static constexpr ScreenSize kWindowSize = { 106, 461 };
|
||||||
|
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
||||||
@@ -136,10 +154,11 @@ namespace OpenRCT2::Ui::Windows
|
|||||||
WIDX_MODE_GROUP,
|
WIDX_MODE_GROUP,
|
||||||
WIDX_CONSTRUCT_ON_LAND,
|
WIDX_CONSTRUCT_ON_LAND,
|
||||||
WIDX_CONSTRUCT_BRIDGE_OR_TUNNEL,
|
WIDX_CONSTRUCT_BRIDGE_OR_TUNNEL,
|
||||||
|
WIDX_CONSTRUCT_DRAG_AREA,
|
||||||
};
|
};
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
static constexpr auto window_footpath_widgets = makeWidgets(
|
static constexpr auto kWindowFootpathWidgets = makeWidgets(
|
||||||
makeWindowShim(kWindowTitle, kWindowSize),
|
makeWindowShim(kWindowTitle, kWindowSize),
|
||||||
|
|
||||||
// Type group
|
// Type group
|
||||||
@@ -164,9 +183,10 @@ namespace OpenRCT2::Ui::Windows
|
|||||||
makeWidget({30, 335}, { 46, 24}, WidgetType::flatBtn, WindowColour::secondary, ImageId(SPR_DEMOLISH_CURRENT_SECTION), STR_REMOVE_PREVIOUS_FOOTPATH_SECTION_TIP ),
|
makeWidget({30, 335}, { 46, 24}, WidgetType::flatBtn, WindowColour::secondary, ImageId(SPR_DEMOLISH_CURRENT_SECTION), STR_REMOVE_PREVIOUS_FOOTPATH_SECTION_TIP ),
|
||||||
|
|
||||||
// Mode group
|
// Mode group
|
||||||
makeWidget({ 3, 361}, {100, 54}, WidgetType::groupbox, WindowColour::primary ),
|
makeWidget({ 3, 361}, {100, 99}, WidgetType::groupbox, WindowColour::primary ),
|
||||||
makeWidget({13, 372}, { 36, 36}, WidgetType::flatBtn, WindowColour::secondary, ImageId(SPR_CONSTRUCTION_FOOTPATH_LAND), STR_CONSTRUCT_FOOTPATH_ON_LAND_TIP ),
|
makeWidget({13, 372}, { 36, 36}, WidgetType::flatBtn, WindowColour::secondary, ImageId(SPR_CONSTRUCTION_FOOTPATH_LAND), STR_CONSTRUCT_FOOTPATH_ON_LAND_TIP ),
|
||||||
makeWidget({57, 372}, { 36, 36}, WidgetType::flatBtn, WindowColour::secondary, ImageId(SPR_CONSTRUCTION_FOOTPATH_BRIDGE), STR_CONSTRUCT_BRIDGE_OR_TUNNEL_FOOTPATH_TIP )
|
makeWidget({57, 372}, { 36, 36}, WidgetType::flatBtn, WindowColour::secondary, ImageId(SPR_CONSTRUCTION_FOOTPATH_BRIDGE), STR_CONSTRUCT_BRIDGE_OR_TUNNEL_FOOTPATH_TIP ),
|
||||||
|
makeWidget({35, 416}, { 36, 36}, WidgetType::flatBtn, WindowColour::secondary, ImageId(SPR_G2_ICON_PATH_DRAG_TOOL), STR_DRAG_AREA_OF_FOOTPATH_TIP )
|
||||||
);
|
);
|
||||||
|
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
@@ -204,19 +224,21 @@ namespace OpenRCT2::Ui::Windows
|
|||||||
|
|
||||||
int32_t _footpathPlaceZ;
|
int32_t _footpathPlaceZ;
|
||||||
|
|
||||||
|
CoordsXY _dragStartPos;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
#pragma region Window Override Events
|
#pragma region Window Override Events
|
||||||
|
|
||||||
void onOpen() override
|
void onOpen() override
|
||||||
{
|
{
|
||||||
setWidgets(window_footpath_widgets);
|
setWidgets(kWindowFootpathWidgets);
|
||||||
|
|
||||||
WindowInitScrollWidgets(*this);
|
WindowInitScrollWidgets(*this);
|
||||||
WindowPushOthersRight(*this);
|
WindowPushOthersRight(*this);
|
||||||
ShowGridlines();
|
ShowGridlines();
|
||||||
|
|
||||||
ToolCancel();
|
ToolCancel();
|
||||||
_footpathConstructionMode = PathConstructionMode::land;
|
_footpathConstructionMode = PathConstructionMode::onLand;
|
||||||
ToolSet(*this, WIDX_CONSTRUCT_ON_LAND, Tool::pathDown);
|
ToolSet(*this, WIDX_CONSTRUCT_ON_LAND, Tool::pathDown);
|
||||||
gInputFlags.set(InputFlag::unk6);
|
gInputFlags.set(InputFlag::unk6);
|
||||||
_footpathErrorOccured = false;
|
_footpathErrorOccured = false;
|
||||||
@@ -253,16 +275,25 @@ namespace OpenRCT2::Ui::Windows
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check tool
|
// Check tool
|
||||||
if (_footpathConstructionMode == PathConstructionMode::land)
|
switch (_footpathConstructionMode)
|
||||||
{
|
{
|
||||||
|
case PathConstructionMode::onLand:
|
||||||
if (!isToolActive(WindowClass::footpath, WIDX_CONSTRUCT_ON_LAND))
|
if (!isToolActive(WindowClass::footpath, WIDX_CONSTRUCT_ON_LAND))
|
||||||
close();
|
close();
|
||||||
}
|
break;
|
||||||
else if (_footpathConstructionMode == PathConstructionMode::bridgeOrTunnelPick)
|
case PathConstructionMode::dragArea:
|
||||||
{
|
break;
|
||||||
|
case PathConstructionMode::bridgeOrTunnelPick:
|
||||||
if (!isToolActive(WindowClass::footpath, WIDX_CONSTRUCT_BRIDGE_OR_TUNNEL))
|
if (!isToolActive(WindowClass::footpath, WIDX_CONSTRUCT_BRIDGE_OR_TUNNEL))
|
||||||
close();
|
close();
|
||||||
|
break;
|
||||||
|
case PathConstructionMode::bridgeOrTunnel:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If another window has enabled a tool, close ours.
|
||||||
|
if (gInputFlags.has(InputFlag::toolActive) && gCurrentToolWidget.windowClassification != WindowClass::footpath)
|
||||||
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
void onMouseDown(WidgetIndex widgetIndex) override
|
void onMouseDown(WidgetIndex widgetIndex) override
|
||||||
@@ -308,6 +339,20 @@ namespace OpenRCT2::Ui::Windows
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void enableDragAreaMode()
|
||||||
|
{
|
||||||
|
_windowFootpathCost = kMoney64Undefined;
|
||||||
|
ToolCancel();
|
||||||
|
FootpathUpdateProvisional();
|
||||||
|
MapInvalidateMapSelectionTiles();
|
||||||
|
gMapSelectFlags.unset(MapSelectFlag::enableConstruct);
|
||||||
|
_footpathConstructionMode = PathConstructionMode::dragArea;
|
||||||
|
ToolSet(*this, WIDX_CONSTRUCT_DRAG_AREA, Tool::pathDown);
|
||||||
|
gInputFlags.set(InputFlag::unk6);
|
||||||
|
_footpathErrorOccured = false;
|
||||||
|
WindowFootpathSetEnabledAndPressedWidgets();
|
||||||
|
}
|
||||||
|
|
||||||
void onMouseUp(WidgetIndex widgetIndex) override
|
void onMouseUp(WidgetIndex widgetIndex) override
|
||||||
{
|
{
|
||||||
switch (widgetIndex)
|
switch (widgetIndex)
|
||||||
@@ -316,7 +361,7 @@ namespace OpenRCT2::Ui::Windows
|
|||||||
close();
|
close();
|
||||||
break;
|
break;
|
||||||
case WIDX_CONSTRUCT_ON_LAND:
|
case WIDX_CONSTRUCT_ON_LAND:
|
||||||
if (_footpathConstructionMode == PathConstructionMode::land)
|
if (_footpathConstructionMode == PathConstructionMode::onLand)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -326,12 +371,20 @@ namespace OpenRCT2::Ui::Windows
|
|||||||
FootpathUpdateProvisional();
|
FootpathUpdateProvisional();
|
||||||
MapInvalidateMapSelectionTiles();
|
MapInvalidateMapSelectionTiles();
|
||||||
gMapSelectFlags.unset(MapSelectFlag::enableConstruct);
|
gMapSelectFlags.unset(MapSelectFlag::enableConstruct);
|
||||||
_footpathConstructionMode = PathConstructionMode::land;
|
_footpathConstructionMode = PathConstructionMode::onLand;
|
||||||
ToolSet(*this, WIDX_CONSTRUCT_ON_LAND, Tool::pathDown);
|
ToolSet(*this, WIDX_CONSTRUCT_ON_LAND, Tool::pathDown);
|
||||||
gInputFlags.set(InputFlag::unk6);
|
gInputFlags.set(InputFlag::unk6);
|
||||||
_footpathErrorOccured = false;
|
_footpathErrorOccured = false;
|
||||||
WindowFootpathSetEnabledAndPressedWidgets();
|
WindowFootpathSetEnabledAndPressedWidgets();
|
||||||
break;
|
break;
|
||||||
|
case WIDX_CONSTRUCT_DRAG_AREA:
|
||||||
|
if (_footpathConstructionMode == PathConstructionMode::dragArea)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
enableDragAreaMode();
|
||||||
|
break;
|
||||||
case WIDX_CONSTRUCT_BRIDGE_OR_TUNNEL:
|
case WIDX_CONSTRUCT_BRIDGE_OR_TUNNEL:
|
||||||
if (_footpathConstructionMode == PathConstructionMode::bridgeOrTunnelPick)
|
if (_footpathConstructionMode == PathConstructionMode::bridgeOrTunnelPick)
|
||||||
{
|
{
|
||||||
@@ -404,6 +457,10 @@ namespace OpenRCT2::Ui::Windows
|
|||||||
{
|
{
|
||||||
WindowFootpathSetProvisionalPathAtPoint(screenCoords);
|
WindowFootpathSetProvisionalPathAtPoint(screenCoords);
|
||||||
}
|
}
|
||||||
|
else if (widgetIndex == WIDX_CONSTRUCT_DRAG_AREA)
|
||||||
|
{
|
||||||
|
WindowFootpathPlaceDragAreaHover(screenCoords);
|
||||||
|
}
|
||||||
else if (widgetIndex == WIDX_CONSTRUCT_BRIDGE_OR_TUNNEL)
|
else if (widgetIndex == WIDX_CONSTRUCT_BRIDGE_OR_TUNNEL)
|
||||||
{
|
{
|
||||||
WindowFootpathSetSelectionStartBridgeAtPoint(screenCoords);
|
WindowFootpathSetSelectionStartBridgeAtPoint(screenCoords);
|
||||||
@@ -416,6 +473,11 @@ namespace OpenRCT2::Ui::Windows
|
|||||||
{
|
{
|
||||||
_footpathErrorOccured = false;
|
_footpathErrorOccured = false;
|
||||||
}
|
}
|
||||||
|
else if (widgetIndex == WIDX_CONSTRUCT_DRAG_AREA)
|
||||||
|
{
|
||||||
|
WindowFootpathPlacePath();
|
||||||
|
_footpathErrorOccured = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void onToolDown(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override
|
void onToolDown(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override
|
||||||
@@ -424,6 +486,10 @@ namespace OpenRCT2::Ui::Windows
|
|||||||
{
|
{
|
||||||
WindowFootpathPlacePathAtPoint(screenCoords);
|
WindowFootpathPlacePathAtPoint(screenCoords);
|
||||||
}
|
}
|
||||||
|
else if (widgetIndex == WIDX_CONSTRUCT_DRAG_AREA)
|
||||||
|
{
|
||||||
|
WindowFootpathPlaceDragAreaSetStart(screenCoords);
|
||||||
|
}
|
||||||
else if (widgetIndex == WIDX_CONSTRUCT_BRIDGE_OR_TUNNEL)
|
else if (widgetIndex == WIDX_CONSTRUCT_BRIDGE_OR_TUNNEL)
|
||||||
{
|
{
|
||||||
WindowFootpathStartBridgeAtPoint(screenCoords);
|
WindowFootpathStartBridgeAtPoint(screenCoords);
|
||||||
@@ -436,6 +502,24 @@ namespace OpenRCT2::Ui::Windows
|
|||||||
{
|
{
|
||||||
WindowFootpathPlacePathAtPoint(screenCoords);
|
WindowFootpathPlacePathAtPoint(screenCoords);
|
||||||
}
|
}
|
||||||
|
else if (widgetIndex == WIDX_CONSTRUCT_DRAG_AREA)
|
||||||
|
{
|
||||||
|
WindowFootpathPlaceDragAreaSetEnd(screenCoords);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void onToolAbort(WidgetIndex widgetIndex) override
|
||||||
|
{
|
||||||
|
// Needs both checks as this will otherwise be triggered when switching from the dragArea tool
|
||||||
|
// to another.
|
||||||
|
if (widgetIndex == WIDX_CONSTRUCT_DRAG_AREA && _footpathConstructionMode == PathConstructionMode::dragArea)
|
||||||
|
{
|
||||||
|
FootpathUpdateProvisional();
|
||||||
|
enableDragAreaMode();
|
||||||
|
// When this tool is aborted, the mouse button is still down.
|
||||||
|
// Set this to prevent a stray piece of path from being placed under the mouse pointer.
|
||||||
|
_footpathErrorOccured = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void onPrepareDraw() override
|
void onPrepareDraw() override
|
||||||
@@ -529,17 +613,6 @@ namespace OpenRCT2::Ui::Windows
|
|||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
||||||
private:
|
private:
|
||||||
FootpathPlacementResult FootpathGetPlacementFromScreenCoords(const ScreenCoordsXY& screenCoords)
|
|
||||||
{
|
|
||||||
if (_footpathPlaceZ > 0)
|
|
||||||
return { _footpathPlaceZ, {} };
|
|
||||||
|
|
||||||
auto info = GetMapCoordinatesFromPos(
|
|
||||||
screenCoords, EnumsToFlags(ViewportInteractionItem::terrain, ViewportInteractionItem::footpath));
|
|
||||||
|
|
||||||
return FootpathGetPlacementFromInfo(info);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* rct2: 0x006A7760
|
* rct2: 0x006A7760
|
||||||
@@ -568,8 +641,10 @@ namespace OpenRCT2::Ui::Windows
|
|||||||
FootpathSlope slope;
|
FootpathSlope slope;
|
||||||
FootpathGetNextPathInfo(&type, footpathLoc, &slope);
|
FootpathGetNextPathInfo(&type, footpathLoc, &slope);
|
||||||
auto pathConstructFlags = FootpathCreateConstructFlags(type);
|
auto pathConstructFlags = FootpathCreateConstructFlags(type);
|
||||||
|
auto tiles = std::array<ProvisionalTile, 1>({ footpathLoc, slope });
|
||||||
|
|
||||||
_windowFootpathCost = FootpathProvisionalSet(type, railings, footpathLoc, slope, pathConstructFlags);
|
_windowFootpathCost = FootpathProvisionalSet(
|
||||||
|
type, railings, footpathLoc, footpathLoc, footpathLoc.z, tiles, pathConstructFlags);
|
||||||
invalidateWidget(WIDX_CONSTRUCT);
|
invalidateWidget(WIDX_CONSTRUCT);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -884,12 +959,8 @@ namespace OpenRCT2::Ui::Windows
|
|||||||
|
|
||||||
if (_footpathPlaceShiftState)
|
if (_footpathPlaceShiftState)
|
||||||
{
|
{
|
||||||
auto surfaceElement = MapGetSurfaceElementAt(mapCoords);
|
auto res = FootpathGetPlacementFromInfo(info);
|
||||||
if (surfaceElement == nullptr)
|
auto mapZ = res.baseZ + _footpathPlaceShiftZ;
|
||||||
return std::nullopt;
|
|
||||||
|
|
||||||
auto mapZ = floor2(surfaceElement->GetBaseZ(), 16);
|
|
||||||
mapZ += _footpathPlaceShiftZ;
|
|
||||||
mapZ = std::max<int16_t>(mapZ, 16);
|
mapZ = std::max<int16_t>(mapZ, 16);
|
||||||
_footpathPlaceZ = mapZ;
|
_footpathPlaceZ = mapZ;
|
||||||
}
|
}
|
||||||
@@ -979,7 +1050,7 @@ namespace OpenRCT2::Ui::Windows
|
|||||||
// Check for change
|
// Check for change
|
||||||
auto provisionalPos = CoordsXYZ(*mapPos, _footpathPlaceZ);
|
auto provisionalPos = CoordsXYZ(*mapPos, _footpathPlaceZ);
|
||||||
if ((_provisionalFootpath.flags.has(ProvisionalPathFlag::placed))
|
if ((_provisionalFootpath.flags.has(ProvisionalPathFlag::placed))
|
||||||
&& _provisionalFootpath.position == provisionalPos)
|
&& _provisionalFootpath.positionA == provisionalPos)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -987,13 +1058,12 @@ namespace OpenRCT2::Ui::Windows
|
|||||||
// Set map selection
|
// Set map selection
|
||||||
gMapSelectFlags.set(MapSelectFlag::enable);
|
gMapSelectFlags.set(MapSelectFlag::enable);
|
||||||
gMapSelectType = MapSelectType::full;
|
gMapSelectType = MapSelectType::full;
|
||||||
gMapSelectPositionA = *mapPos;
|
|
||||||
gMapSelectPositionB = *mapPos;
|
|
||||||
|
|
||||||
|
setMapSelectRange(*mapPos);
|
||||||
FootpathUpdateProvisional();
|
FootpathUpdateProvisional();
|
||||||
|
|
||||||
// Figure out what slope and height to use
|
// Figure out what slope and height to use
|
||||||
auto placement = FootpathGetPlacementFromScreenCoords(screenCoords);
|
auto placement = WindowFootpathGetPlacementFromScreenCoords(screenCoords);
|
||||||
if (!placement.isValid())
|
if (!placement.isValid())
|
||||||
{
|
{
|
||||||
gMapSelectFlags.unset(MapSelectFlag::enable);
|
gMapSelectFlags.unset(MapSelectFlag::enable);
|
||||||
@@ -1004,8 +1074,61 @@ namespace OpenRCT2::Ui::Windows
|
|||||||
// Set provisional path
|
// Set provisional path
|
||||||
auto pathType = gFootpathSelection.GetSelectedSurface();
|
auto pathType = gFootpathSelection.GetSelectedSurface();
|
||||||
auto constructFlags = FootpathCreateConstructFlags(pathType);
|
auto constructFlags = FootpathCreateConstructFlags(pathType);
|
||||||
|
auto footpathLoc = CoordsXYZ(*mapPos, placement.baseZ);
|
||||||
|
auto tiles = std::array<ProvisionalTile, 1>({ footpathLoc, placement.slope });
|
||||||
const auto footpathCost = FootpathProvisionalSet(
|
const auto footpathCost = FootpathProvisionalSet(
|
||||||
pathType, gFootpathSelection.Railings, { *mapPos, placement.baseZ }, placement.slope, constructFlags);
|
pathType, gFootpathSelection.Railings, *mapPos, *mapPos, placement.baseZ, tiles, constructFlags);
|
||||||
|
|
||||||
|
if (_windowFootpathCost != footpathCost)
|
||||||
|
{
|
||||||
|
_windowFootpathCost = footpathCost;
|
||||||
|
invalidateWidget(WIDX_CONSTRUCT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::vector<ProvisionalTile> buildTileVector(MapRange range, int32_t baseZ)
|
||||||
|
{
|
||||||
|
std::vector<ProvisionalTile> tiles{};
|
||||||
|
for (auto y = range.GetY1(); y <= range.GetY2(); y += kCoordsXYStep)
|
||||||
|
{
|
||||||
|
for (auto x = range.GetX1(); x <= range.GetX2(); x += kCoordsXYStep)
|
||||||
|
{
|
||||||
|
FootpathPlacementResult placement = { baseZ, {} };
|
||||||
|
if (baseZ == 0)
|
||||||
|
placement = FootpathGetOnTerrainPlacement(TileCoordsXY(CoordsXY(x, y)));
|
||||||
|
|
||||||
|
auto calculatedLocation = CoordsXYZ(x, y, placement.baseZ);
|
||||||
|
tiles.push_back({ calculatedLocation, placement.slope });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WindowFootpathSetProvisionalPathDragArea(MapRange range, int32_t baseZ)
|
||||||
|
{
|
||||||
|
MapInvalidateSelectionRect();
|
||||||
|
gMapSelectFlags.unset(MapSelectFlag::enableArrow);
|
||||||
|
|
||||||
|
// Check for change
|
||||||
|
if ((_provisionalFootpath.flags.has(ProvisionalPathFlag::placed)) && range.Point1 == _provisionalFootpath.positionA
|
||||||
|
&& range.Point2 == _provisionalFootpath.positionB && baseZ == _provisionalFootpath.startZ)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set map selection
|
||||||
|
gMapSelectFlags.set(MapSelectFlag::enable);
|
||||||
|
gMapSelectType = MapSelectType::full;
|
||||||
|
|
||||||
|
FootpathUpdateProvisional();
|
||||||
|
|
||||||
|
// Set provisional path
|
||||||
|
auto tiles = buildTileVector(range, baseZ);
|
||||||
|
auto pathType = gFootpathSelection.GetSelectedSurface();
|
||||||
|
auto constructFlags = FootpathCreateConstructFlags(pathType);
|
||||||
|
const auto footpathCost = FootpathProvisionalSet(
|
||||||
|
pathType, gFootpathSelection.Railings, range.Point1, range.Point2, baseZ, tiles, constructFlags);
|
||||||
|
|
||||||
if (_windowFootpathCost != footpathCost)
|
if (_windowFootpathCost != footpathCost)
|
||||||
{
|
{
|
||||||
@@ -1034,8 +1157,7 @@ namespace OpenRCT2::Ui::Windows
|
|||||||
|
|
||||||
gMapSelectFlags.set(MapSelectFlag::enable, MapSelectFlag::enableArrow);
|
gMapSelectFlags.set(MapSelectFlag::enable, MapSelectFlag::enableArrow);
|
||||||
gMapSelectType = MapSelectType::full;
|
gMapSelectType = MapSelectType::full;
|
||||||
gMapSelectPositionA = mapCoords;
|
setMapSelectRange(mapCoords);
|
||||||
gMapSelectPositionB = mapCoords;
|
|
||||||
|
|
||||||
int32_t z = tileElement->GetBaseZ();
|
int32_t z = tileElement->GetBaseZ();
|
||||||
|
|
||||||
@@ -1056,6 +1178,155 @@ namespace OpenRCT2::Ui::Windows
|
|||||||
MapInvalidateSelectionRect();
|
MapInvalidateSelectionRect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FootpathPlacementResult WindowFootpathGetPlacementFromScreenCoords(const ScreenCoordsXY& screenCoords)
|
||||||
|
{
|
||||||
|
if (_footpathPlaceZ != 0)
|
||||||
|
return { _footpathPlaceZ, { FootpathSlopeType::flat } };
|
||||||
|
|
||||||
|
const auto info = GetMapCoordinatesFromPos(
|
||||||
|
screenCoords, EnumsToFlags(ViewportInteractionItem::terrain, ViewportInteractionItem::footpath));
|
||||||
|
return FootpathGetPlacementFromInfo(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WindowFootpathPlaceDragAreaSetStart(const ScreenCoordsXY& screenCoords)
|
||||||
|
{
|
||||||
|
if (_footpathErrorOccured)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto mapPos = FootpathGetPlacePositionFromScreenPosition(screenCoords);
|
||||||
|
if (!mapPos)
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto placement = WindowFootpathGetPlacementFromScreenCoords(screenCoords);
|
||||||
|
if (placement.baseZ > 0 && placement.slope.type == FootpathSlopeType::flat)
|
||||||
|
_footpathPlaceZ = placement.baseZ;
|
||||||
|
|
||||||
|
gMapSelectFlags.set(MapSelectFlag::enable);
|
||||||
|
gMapSelectType = MapSelectType::full;
|
||||||
|
|
||||||
|
auto correctedPosition = mapPos->ToTileStart();
|
||||||
|
setMapSelectRange(correctedPosition);
|
||||||
|
_dragStartPos = correctedPosition;
|
||||||
|
WindowFootpathSetProvisionalPathDragArea(getMapSelectRange(), placement.baseZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WindowFootpathPlaceDragAreaSetEnd(const ScreenCoordsXY& screenCoords)
|
||||||
|
{
|
||||||
|
if (_footpathErrorOccured)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<CoordsXY> mapPos;
|
||||||
|
if (_footpathPlaceShiftState)
|
||||||
|
mapPos = ScreenGetMapXYWithZ(screenCoords, _footpathPlaceZ);
|
||||||
|
else
|
||||||
|
mapPos = FootpathGetPlacePositionFromScreenPosition(screenCoords);
|
||||||
|
|
||||||
|
if (!mapPos)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto correctedPos = mapPos->ToTileStart();
|
||||||
|
|
||||||
|
// For queues, only allow selecting a single line.
|
||||||
|
if (gFootpathSelection.IsQueueSelected)
|
||||||
|
{
|
||||||
|
auto xDiff = correctedPos.x - _dragStartPos.x;
|
||||||
|
auto yDiff = correctedPos.y - _dragStartPos.y;
|
||||||
|
if (std::abs(xDiff) > std::abs(yDiff))
|
||||||
|
{
|
||||||
|
correctedPos.y = _dragStartPos.y;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
correctedPos.x = _dragStartPos.x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setMapSelectRange({ _dragStartPos, correctedPos });
|
||||||
|
WindowFootpathSetProvisionalPathDragArea(getMapSelectRange(), _footpathPlaceZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WindowFootpathPlaceDragAreaHover(const ScreenCoordsXY& screenCoords)
|
||||||
|
{
|
||||||
|
bool isLeftMousePressed = gInputFlags.has(InputFlag::leftMousePressed);
|
||||||
|
if (isLeftMousePressed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
MapInvalidateSelectionRect();
|
||||||
|
gMapSelectFlags.unset(MapSelectFlag::enableArrow);
|
||||||
|
|
||||||
|
auto mapPos = FootpathGetPlacePositionFromScreenPosition(screenCoords);
|
||||||
|
if (!mapPos)
|
||||||
|
{
|
||||||
|
gMapSelectFlags.unset(MapSelectFlag::enable);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto placement = WindowFootpathGetPlacementFromScreenCoords(screenCoords);
|
||||||
|
|
||||||
|
// Set map selection
|
||||||
|
gMapSelectFlags.set(MapSelectFlag::enable);
|
||||||
|
gMapSelectType = MapSelectType::full;
|
||||||
|
|
||||||
|
setMapSelectRange(*mapPos);
|
||||||
|
WindowFootpathSetProvisionalPathDragArea(getMapSelectRange(), placement.baseZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WindowFootpathPlacePath()
|
||||||
|
{
|
||||||
|
if (_footpathErrorOccured)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FootpathUpdateProvisional();
|
||||||
|
if (_provisionalFootpath.tiles.size() == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Try and place path
|
||||||
|
auto selectedType = gFootpathSelection.GetSelectedSurface();
|
||||||
|
PathConstructFlags constructFlags = FootpathCreateConstructFlags(selectedType);
|
||||||
|
|
||||||
|
bool anySuccess = false;
|
||||||
|
money64 cost = 0;
|
||||||
|
CoordsXYZ lastLocation;
|
||||||
|
|
||||||
|
for (const auto& tile : _provisionalFootpath.tiles)
|
||||||
|
{
|
||||||
|
auto footpathPlaceAction = GameActions::FootpathPlaceAction(
|
||||||
|
tile.position, tile.slope, selectedType, gFootpathSelection.Railings, kInvalidDirection, constructFlags);
|
||||||
|
footpathPlaceAction.SetCallback(
|
||||||
|
[&anySuccess, &cost](const GameActions::GameAction* ga, const GameActions::Result* result) {
|
||||||
|
if (result->Error == GameActions::Status::Ok)
|
||||||
|
{
|
||||||
|
anySuccess = true;
|
||||||
|
cost += result->Cost;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
GameActions::Execute(&footpathPlaceAction, getGameState());
|
||||||
|
lastLocation = tile.position;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (anySuccess)
|
||||||
|
{
|
||||||
|
// Don't play sound if it is no cost to prevent multiple sounds. TODO: make this work in no
|
||||||
|
// money scenarios
|
||||||
|
if (cost != 0)
|
||||||
|
{
|
||||||
|
Audio::Play3D(Audio::SoundId::placeItem, lastLocation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_footpathErrorOccured = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* rct2: 0x006A82C5
|
* rct2: 0x006A82C5
|
||||||
@@ -1073,7 +1344,7 @@ namespace OpenRCT2::Ui::Windows
|
|||||||
if (!mapPos)
|
if (!mapPos)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto placement = FootpathGetPlacementFromScreenCoords(screenCoords);
|
auto placement = WindowFootpathGetPlacementFromScreenCoords(screenCoords);
|
||||||
|
|
||||||
// Try and place path
|
// Try and place path
|
||||||
auto selectedType = gFootpathSelection.GetSelectedSurface();
|
auto selectedType = gFootpathSelection.GetSelectedSurface();
|
||||||
@@ -1685,24 +1956,38 @@ namespace OpenRCT2::Ui::Windows
|
|||||||
* rct2: 0x006A76FF
|
* rct2: 0x006A76FF
|
||||||
*/
|
*/
|
||||||
static money64 FootpathProvisionalSet(
|
static money64 FootpathProvisionalSet(
|
||||||
ObjectEntryIndex type, ObjectEntryIndex railingsType, const CoordsXYZ& footpathLoc, FootpathSlope slope,
|
ObjectEntryIndex type, ObjectEntryIndex railingsType, const CoordsXY& footpathLocA, const CoordsXY& footpathLocB,
|
||||||
PathConstructFlags constructFlags)
|
int32_t startZ, const std::span<const ProvisionalTile> tiles, PathConstructFlags constructFlags)
|
||||||
{
|
{
|
||||||
FootpathRemoveProvisional();
|
FootpathRemoveProvisional();
|
||||||
|
|
||||||
|
GameActions::Result res;
|
||||||
|
std::vector<ProvisionalTile> succesfulTiles{};
|
||||||
|
money64 cost = 0;
|
||||||
|
for (const auto& tile : tiles)
|
||||||
|
{
|
||||||
auto footpathPlaceAction = GameActions::FootpathPlaceAction(
|
auto footpathPlaceAction = GameActions::FootpathPlaceAction(
|
||||||
footpathLoc, slope, type, railingsType, kInvalidDirection, constructFlags);
|
tile.position, tile.slope, type, railingsType, kInvalidDirection, constructFlags);
|
||||||
footpathPlaceAction.SetFlags(GAME_COMMAND_FLAG_GHOST | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED);
|
footpathPlaceAction.SetFlags(GAME_COMMAND_FLAG_GHOST | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED);
|
||||||
|
res = GameActions::Execute(&footpathPlaceAction, getGameState());
|
||||||
|
// The latter status will be returned if there is already path at the tile in question, to prevent a ghost
|
||||||
|
// from glitching through the existing path.
|
||||||
|
if (res.Error == GameActions::Status::Ok || res.Error == GameActions::Status::ItemAlreadyPlaced)
|
||||||
|
{
|
||||||
|
succesfulTiles.emplace_back(tile);
|
||||||
|
cost += res.Cost;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
auto res = GameActions::Execute(&footpathPlaceAction, getGameState());
|
bool anySuccesful = succesfulTiles.size() > 0;
|
||||||
money64 cost = res.Error == GameActions::Status::Ok ? res.Cost : kMoney64Undefined;
|
if (anySuccesful)
|
||||||
|
|
||||||
if (res.Error == GameActions::Status::Ok)
|
|
||||||
{
|
{
|
||||||
_provisionalFootpath.surfaceIndex = type;
|
_provisionalFootpath.surfaceIndex = type;
|
||||||
_provisionalFootpath.railingsIndex = railingsType;
|
_provisionalFootpath.railingsIndex = railingsType;
|
||||||
_provisionalFootpath.position = footpathLoc;
|
_provisionalFootpath.positionA = footpathLocA;
|
||||||
_provisionalFootpath.slope = slope;
|
_provisionalFootpath.positionB = footpathLocB;
|
||||||
|
_provisionalFootpath.startZ = startZ;
|
||||||
|
_provisionalFootpath.tiles = succesfulTiles;
|
||||||
_provisionalFootpath.constructFlags = constructFlags;
|
_provisionalFootpath.constructFlags = constructFlags;
|
||||||
_provisionalFootpath.flags.set(ProvisionalPathFlag::placed);
|
_provisionalFootpath.flags.set(ProvisionalPathFlag::placed);
|
||||||
|
|
||||||
@@ -1718,26 +2003,26 @@ namespace OpenRCT2::Ui::Windows
|
|||||||
|
|
||||||
if (!isToolActive(WindowClass::scenery))
|
if (!isToolActive(WindowClass::scenery))
|
||||||
{
|
{
|
||||||
if (res.Error != GameActions::Status::Ok)
|
if (succesfulTiles.size() == 0)
|
||||||
{
|
{
|
||||||
// If we can't build this, don't show a virtual floor.
|
// If we can't build this, don't show a virtual floor.
|
||||||
VirtualFloorSetHeight(0);
|
VirtualFloorSetHeight(0);
|
||||||
}
|
}
|
||||||
else if (
|
else if (
|
||||||
_provisionalFootpath.slope.type == FootpathSlopeType::flat
|
succesfulTiles[0].slope.type == FootpathSlopeType::flat
|
||||||
|| _provisionalFootpath.position.z < _footpathConstructFromPosition.z)
|
|| succesfulTiles[0].position.z < _footpathConstructFromPosition.z)
|
||||||
{
|
{
|
||||||
// Going either straight on, or down.
|
// Going either straight on, or down.
|
||||||
VirtualFloorSetHeight(_provisionalFootpath.position.z);
|
VirtualFloorSetHeight(succesfulTiles[0].position.z);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Going up in the world!
|
// Going up in the world!
|
||||||
VirtualFloorSetHeight(_provisionalFootpath.position.z + kLandHeightStep);
|
VirtualFloorSetHeight(succesfulTiles[0].position.z + kLandHeightStep);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return cost;
|
return anySuccesful ? cost : kMoney64Undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1746,11 +2031,14 @@ namespace OpenRCT2::Ui::Windows
|
|||||||
*/
|
*/
|
||||||
void FootpathRemoveProvisional()
|
void FootpathRemoveProvisional()
|
||||||
{
|
{
|
||||||
if (_provisionalFootpath.flags.has(ProvisionalPathFlag::placed))
|
if (!_provisionalFootpath.flags.has(ProvisionalPathFlag::placed))
|
||||||
{
|
return;
|
||||||
|
|
||||||
_provisionalFootpath.flags.unset(ProvisionalPathFlag::placed);
|
_provisionalFootpath.flags.unset(ProvisionalPathFlag::placed);
|
||||||
|
|
||||||
auto action = GameActions::FootpathRemoveAction(_provisionalFootpath.position);
|
for (const auto& tile : _provisionalFootpath.tiles)
|
||||||
|
{
|
||||||
|
auto action = GameActions::FootpathRemoveAction(tile.position);
|
||||||
action.SetFlags(GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_NO_SPEND | GAME_COMMAND_FLAG_GHOST);
|
action.SetFlags(GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_NO_SPEND | GAME_COMMAND_FLAG_GHOST);
|
||||||
GameActions::Execute(&action, getGameState());
|
GameActions::Execute(&action, getGameState());
|
||||||
}
|
}
|
||||||
@@ -1787,8 +2075,9 @@ namespace OpenRCT2::Ui::Windows
|
|||||||
{
|
{
|
||||||
_provisionalFootpath.flags.unset(ProvisionalPathFlag::placed);
|
_provisionalFootpath.flags.unset(ProvisionalPathFlag::placed);
|
||||||
FootpathProvisionalSet(
|
FootpathProvisionalSet(
|
||||||
_provisionalFootpath.surfaceIndex, _provisionalFootpath.railingsIndex, _provisionalFootpath.position,
|
_provisionalFootpath.surfaceIndex, _provisionalFootpath.railingsIndex, _provisionalFootpath.positionA,
|
||||||
_provisionalFootpath.slope, _provisionalFootpath.constructFlags);
|
_provisionalFootpath.positionB, _provisionalFootpath.startZ, _provisionalFootpath.tiles,
|
||||||
|
_provisionalFootpath.constructFlags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1084,6 +1084,7 @@ enum : ImageIndex
|
|||||||
SPR_G2_ICON_MEDIUM_CURVE_LEFT = SPR_G2_OPAQUE_WATER_OVERLAY + 5,
|
SPR_G2_ICON_MEDIUM_CURVE_LEFT = SPR_G2_OPAQUE_WATER_OVERLAY + 5,
|
||||||
SPR_G2_ICON_MEDIUM_CURVE_RIGHT,
|
SPR_G2_ICON_MEDIUM_CURVE_RIGHT,
|
||||||
SPR_G2_ICON_ROTATE_ANTI_CLOCKWISE,
|
SPR_G2_ICON_ROTATE_ANTI_CLOCKWISE,
|
||||||
|
SPR_G2_ICON_PATH_DRAG_TOOL,
|
||||||
|
|
||||||
SPR_G2_END,
|
SPR_G2_END,
|
||||||
|
|
||||||
|
|||||||
@@ -225,7 +225,7 @@ namespace OpenRCT2::GameActions
|
|||||||
|
|
||||||
if (GetFlags() & GAME_COMMAND_FLAG_GHOST && !pathElement->IsGhost())
|
if (GetFlags() & GAME_COMMAND_FLAG_GHOST && !pathElement->IsGhost())
|
||||||
{
|
{
|
||||||
return Result(Status::Unknown, STR_CANT_BUILD_FOOTPATH_HERE, kStringIdNone);
|
return Result(Status::ItemAlreadyPlaced, STR_CANT_BUILD_FOOTPATH_HERE, kStringIdNone);
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,6 +95,11 @@ struct FootpathSlope
|
|||||||
{
|
{
|
||||||
FootpathSlopeType type{};
|
FootpathSlopeType type{};
|
||||||
Direction direction{};
|
Direction direction{};
|
||||||
|
|
||||||
|
constexpr bool operator==(const FootpathSlope& rhs) const
|
||||||
|
{
|
||||||
|
return type == rhs.type && direction == rhs.direction;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FootpathPlacementResult
|
struct FootpathPlacementResult
|
||||||
|
|||||||
@@ -57,3 +57,20 @@ void MapInvalidateSelectionRect()
|
|||||||
|
|
||||||
OpenRCT2::ViewportsInvalidate({ { left, top }, { right, bottom } });
|
OpenRCT2::ViewportsInvalidate({ { left, top }, { right, bottom } });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MapRange getMapSelectRange()
|
||||||
|
{
|
||||||
|
return MapRange(gMapSelectPositionA, gMapSelectPositionB);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setMapSelectRange(const MapRange& range)
|
||||||
|
{
|
||||||
|
const auto normalised = range.Normalise();
|
||||||
|
gMapSelectPositionA = normalised.Point1;
|
||||||
|
gMapSelectPositionB = normalised.Point2;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setMapSelectRange(const CoordsXY coords)
|
||||||
|
{
|
||||||
|
setMapSelectRange({ coords, coords });
|
||||||
|
}
|
||||||
|
|||||||
@@ -65,3 +65,6 @@ extern std::vector<CoordsXY> gMapSelectionTiles;
|
|||||||
|
|
||||||
void MapInvalidateMapSelectionTiles();
|
void MapInvalidateMapSelectionTiles();
|
||||||
void MapInvalidateSelectionRect();
|
void MapInvalidateSelectionRect();
|
||||||
|
MapRange getMapSelectRange();
|
||||||
|
void setMapSelectRange(const MapRange& range);
|
||||||
|
void setMapSelectRange(const CoordsXY coords);
|
||||||
|
|||||||
Reference in New Issue
Block a user