From 7a30169c283c182dfdd83dac13673213af65f249 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=CE=B6eh=20Matt?= <5415177+ZehMatt@users.noreply.github.com>
Date: Wed, 20 Oct 2021 21:47:56 +0300
Subject: [PATCH] Refactor out construction clearance into a new compilation
unit
---
src/openrct2/actions/FootpathPlaceAction.cpp | 1 +
.../actions/FootpathPlaceFromTrackAction.cpp | 1 +
src/openrct2/actions/LandSetHeightAction.cpp | 1 +
.../actions/LargeSceneryPlaceAction.cpp | 1 +
src/openrct2/actions/MazePlaceTrackAction.cpp | 1 +
src/openrct2/actions/MazeSetTrackAction.cpp | 1 +
.../actions/PlaceParkEntranceAction.cpp | 1 +
.../actions/RideEntranceExitPlaceAction.cpp | 1 +
.../actions/SmallSceneryPlaceAction.cpp | 1 +
src/openrct2/actions/TrackPlaceAction.cpp | 1 +
src/openrct2/actions/WallPlaceAction.cpp | 1 +
src/openrct2/actions/WaterSetHeightAction.cpp | 1 +
src/openrct2/libopenrct2.vcxproj | 2 +
src/openrct2/peep/Peep.cpp | 1 +
src/openrct2/world/ConstructionClearance.cpp | 359 ++++++++++++++++++
src/openrct2/world/ConstructionClearance.h | 25 ++
src/openrct2/world/Map.cpp | 286 --------------
src/openrct2/world/Map.h | 16 -
src/openrct2/world/SmallScenery.cpp | 52 ---
19 files changed, 399 insertions(+), 354 deletions(-)
create mode 100644 src/openrct2/world/ConstructionClearance.cpp
create mode 100644 src/openrct2/world/ConstructionClearance.h
diff --git a/src/openrct2/actions/FootpathPlaceAction.cpp b/src/openrct2/actions/FootpathPlaceAction.cpp
index df93c33327..b83e5062f7 100644
--- a/src/openrct2/actions/FootpathPlaceAction.cpp
+++ b/src/openrct2/actions/FootpathPlaceAction.cpp
@@ -15,6 +15,7 @@
#include "../interface/Window.h"
#include "../localisation/StringIds.h"
#include "../management/Finance.h"
+#include "../world/ConstructionClearance.h"
#include "../world/Footpath.h"
#include "../world/Location.hpp"
#include "../world/Park.h"
diff --git a/src/openrct2/actions/FootpathPlaceFromTrackAction.cpp b/src/openrct2/actions/FootpathPlaceFromTrackAction.cpp
index 80a67d0ddb..295e05e166 100644
--- a/src/openrct2/actions/FootpathPlaceFromTrackAction.cpp
+++ b/src/openrct2/actions/FootpathPlaceFromTrackAction.cpp
@@ -15,6 +15,7 @@
#include "../interface/Window.h"
#include "../localisation/StringIds.h"
#include "../management/Finance.h"
+#include "../world/ConstructionClearance.h"
#include "../world/Footpath.h"
#include "../world/Location.hpp"
#include "../world/Park.h"
diff --git a/src/openrct2/actions/LandSetHeightAction.cpp b/src/openrct2/actions/LandSetHeightAction.cpp
index 02c6c35b02..aa43b243c3 100644
--- a/src/openrct2/actions/LandSetHeightAction.cpp
+++ b/src/openrct2/actions/LandSetHeightAction.cpp
@@ -17,6 +17,7 @@
#include "../management/Finance.h"
#include "../ride/RideData.h"
#include "../windows/Intent.h"
+#include "../world/ConstructionClearance.h"
#include "../world/Park.h"
#include "../world/Scenery.h"
#include "../world/SmallScenery.h"
diff --git a/src/openrct2/actions/LargeSceneryPlaceAction.cpp b/src/openrct2/actions/LargeSceneryPlaceAction.cpp
index 51b3801cec..8fc746df28 100644
--- a/src/openrct2/actions/LargeSceneryPlaceAction.cpp
+++ b/src/openrct2/actions/LargeSceneryPlaceAction.cpp
@@ -14,6 +14,7 @@
#include "../object/ObjectLimits.h"
#include "../ride/Ride.h"
#include "../world/Banner.h"
+#include "../world/ConstructionClearance.h"
#include "../world/MapAnimation.h"
#include "../world/Surface.h"
diff --git a/src/openrct2/actions/MazePlaceTrackAction.cpp b/src/openrct2/actions/MazePlaceTrackAction.cpp
index a2e01c3642..ed7da150e9 100644
--- a/src/openrct2/actions/MazePlaceTrackAction.cpp
+++ b/src/openrct2/actions/MazePlaceTrackAction.cpp
@@ -11,6 +11,7 @@
#include "../management/Finance.h"
#include "../ride/RideData.h"
#include "../ride/TrackData.h"
+#include "../world/ConstructionClearance.h"
using namespace OpenRCT2::TrackMetaData;
diff --git a/src/openrct2/actions/MazeSetTrackAction.cpp b/src/openrct2/actions/MazeSetTrackAction.cpp
index 2d740b4de6..8f47f6eb49 100644
--- a/src/openrct2/actions/MazeSetTrackAction.cpp
+++ b/src/openrct2/actions/MazeSetTrackAction.cpp
@@ -18,6 +18,7 @@
#include "../ride/RideData.h"
#include "../ride/Track.h"
#include "../ride/TrackData.h"
+#include "../world/ConstructionClearance.h"
#include "../world/Footpath.h"
#include "../world/Park.h"
diff --git a/src/openrct2/actions/PlaceParkEntranceAction.cpp b/src/openrct2/actions/PlaceParkEntranceAction.cpp
index 0603218380..93f1eb82ef 100644
--- a/src/openrct2/actions/PlaceParkEntranceAction.cpp
+++ b/src/openrct2/actions/PlaceParkEntranceAction.cpp
@@ -14,6 +14,7 @@
#include "../core/MemoryStream.h"
#include "../localisation/StringIds.h"
#include "../management/Finance.h"
+#include "../world/ConstructionClearance.h"
#include "../world/Entrance.h"
#include "../world/Footpath.h"
#include "../world/MapAnimation.h"
diff --git a/src/openrct2/actions/RideEntranceExitPlaceAction.cpp b/src/openrct2/actions/RideEntranceExitPlaceAction.cpp
index df49331e73..c98366ea6a 100644
--- a/src/openrct2/actions/RideEntranceExitPlaceAction.cpp
+++ b/src/openrct2/actions/RideEntranceExitPlaceAction.cpp
@@ -13,6 +13,7 @@
#include "../management/Finance.h"
#include "../ride/Ride.h"
#include "../ride/Station.h"
+#include "../world/ConstructionClearance.h"
#include "../world/MapAnimation.h"
RideEntranceExitPlaceAction::RideEntranceExitPlaceAction(
diff --git a/src/openrct2/actions/SmallSceneryPlaceAction.cpp b/src/openrct2/actions/SmallSceneryPlaceAction.cpp
index e6e536d19b..92a819ec11 100644
--- a/src/openrct2/actions/SmallSceneryPlaceAction.cpp
+++ b/src/openrct2/actions/SmallSceneryPlaceAction.cpp
@@ -19,6 +19,7 @@
#include "../management/Finance.h"
#include "../ride/Ride.h"
#include "../ride/TrackDesign.h"
+#include "../world/ConstructionClearance.h"
#include "../world/MapAnimation.h"
#include "../world/Park.h"
#include "../world/SmallScenery.h"
diff --git a/src/openrct2/actions/TrackPlaceAction.cpp b/src/openrct2/actions/TrackPlaceAction.cpp
index ec46e18123..e8ad36a7c0 100644
--- a/src/openrct2/actions/TrackPlaceAction.cpp
+++ b/src/openrct2/actions/TrackPlaceAction.cpp
@@ -16,6 +16,7 @@
#include "../ride/TrackData.h"
#include "../ride/TrackDesign.h"
#include "../util/Math.hpp"
+#include "../world/ConstructionClearance.h"
#include "../world/MapAnimation.h"
#include "../world/Surface.h"
#include "RideSetSettingAction.h"
diff --git a/src/openrct2/actions/WallPlaceAction.cpp b/src/openrct2/actions/WallPlaceAction.cpp
index fca2f02f11..5b528b2934 100644
--- a/src/openrct2/actions/WallPlaceAction.cpp
+++ b/src/openrct2/actions/WallPlaceAction.cpp
@@ -14,6 +14,7 @@
#include "../ride/Track.h"
#include "../ride/TrackDesign.h"
#include "../world/Banner.h"
+#include "../world/ConstructionClearance.h"
#include "../world/LargeScenery.h"
#include "../world/MapAnimation.h"
#include "../world/SmallScenery.h"
diff --git a/src/openrct2/actions/WaterSetHeightAction.cpp b/src/openrct2/actions/WaterSetHeightAction.cpp
index 523ab0b358..298df52762 100644
--- a/src/openrct2/actions/WaterSetHeightAction.cpp
+++ b/src/openrct2/actions/WaterSetHeightAction.cpp
@@ -11,6 +11,7 @@
#include "../OpenRCT2.h"
#include "../management/Finance.h"
+#include "../world/ConstructionClearance.h"
#include "../world/Park.h"
#include "../world/Surface.h"
diff --git a/src/openrct2/libopenrct2.vcxproj b/src/openrct2/libopenrct2.vcxproj
index 26dcee802b..cc3067e1e5 100644
--- a/src/openrct2/libopenrct2.vcxproj
+++ b/src/openrct2/libopenrct2.vcxproj
@@ -466,6 +466,7 @@
+
@@ -911,6 +912,7 @@
+
diff --git a/src/openrct2/peep/Peep.cpp b/src/openrct2/peep/Peep.cpp
index e5b1ba67d8..1a7eb5a5d4 100644
--- a/src/openrct2/peep/Peep.cpp
+++ b/src/openrct2/peep/Peep.cpp
@@ -36,6 +36,7 @@
#include "../windows/Intent.h"
#include "../world/Balloon.h"
#include "../world/Climate.h"
+#include "../world/ConstructionClearance.h"
#include "../world/EntityTweener.h"
#include "../world/Entrance.h"
#include "../world/Footpath.h"
diff --git a/src/openrct2/world/ConstructionClearance.cpp b/src/openrct2/world/ConstructionClearance.cpp
new file mode 100644
index 0000000000..430119a991
--- /dev/null
+++ b/src/openrct2/world/ConstructionClearance.cpp
@@ -0,0 +1,359 @@
+/*****************************************************************************
+ * Copyright (c) 2014-2021 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 "ConstructionClearance.h"
+
+#include "../Game.h"
+#include "../openrct2/Cheats.h"
+#include "../ride/Ride.h"
+#include "../ride/RideData.h"
+#include "Map.h"
+#include "Park.h"
+#include "Scenery.h"
+#include "SmallScenery.h"
+#include "Surface.h"
+
+static int32_t map_place_clear_func(
+ TileElement** tile_element, const CoordsXY& coords, uint8_t flags, money32* price, bool is_scenery)
+{
+ if ((*tile_element)->GetType() != TILE_ELEMENT_TYPE_SMALL_SCENERY)
+ return 1;
+
+ if (is_scenery && !(flags & GAME_COMMAND_FLAG_PATH_SCENERY))
+ return 1;
+
+ auto* scenery = (*tile_element)->AsSmallScenery()->GetEntry();
+
+ if (gParkFlags & PARK_FLAGS_FORBID_TREE_REMOVAL)
+ {
+ if (scenery != nullptr && scenery->HasFlag(SMALL_SCENERY_FLAG_IS_TREE))
+ return 1;
+ }
+
+ if (!(gParkFlags & PARK_FLAGS_NO_MONEY) && scenery != nullptr)
+ *price += scenery->removal_price * 10;
+
+ if (flags & GAME_COMMAND_FLAG_GHOST)
+ return 0;
+
+ if (!(flags & GAME_COMMAND_FLAG_APPLY))
+ return 0;
+
+ map_invalidate_tile({ coords, (*tile_element)->GetBaseZ(), (*tile_element)->GetClearanceZ() });
+
+ tile_element_remove(*tile_element);
+
+ (*tile_element)--;
+ return 0;
+}
+
+/**
+ *
+ * rct2: 0x006E0D6E, 0x006B8D88
+ */
+int32_t map_place_scenery_clear_func(TileElement** tile_element, const CoordsXY& coords, uint8_t flags, money32* price)
+{
+ return map_place_clear_func(tile_element, coords, flags, price, /*is_scenery=*/true);
+}
+
+/**
+ *
+ * rct2: 0x006C5A4F, 0x006CDE57, 0x006A6733, 0x0066637E
+ */
+int32_t map_place_non_scenery_clear_func(TileElement** tile_element, const CoordsXY& coords, uint8_t flags, money32* price)
+{
+ return map_place_clear_func(tile_element, coords, flags, price, /*is_scenery=*/false);
+}
+
+static bool MapLoc68BABCShouldContinue(
+ TileElement* tileElement, const CoordsXYRangedZ& pos, CLEAR_FUNC clearFunc, uint8_t flags, money32& price,
+ uint8_t crossingMode, bool canBuildCrossing)
+{
+ if (clearFunc != nullptr)
+ {
+ if (!clearFunc(&tileElement, pos, flags, &price))
+ {
+ return true;
+ }
+ }
+
+ // Crossing mode 1: building track over path
+ if (crossingMode == 1 && canBuildCrossing && tileElement->GetType() == TILE_ELEMENT_TYPE_PATH
+ && tileElement->GetBaseZ() == pos.baseZ && !tileElement->AsPath()->IsQueue() && !tileElement->AsPath()->IsSloped())
+ {
+ return true;
+ }
+ // Crossing mode 2: building path over track
+ else if (
+ crossingMode == 2 && canBuildCrossing && tileElement->GetType() == TILE_ELEMENT_TYPE_TRACK
+ && tileElement->GetBaseZ() == pos.baseZ && tileElement->AsTrack()->GetTrackType() == TrackElemType::Flat)
+ {
+ auto ride = get_ride(tileElement->AsTrack()->GetRideIndex());
+ if (ride != nullptr && ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_SUPPORTS_LEVEL_CROSSINGS))
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/**
+ *
+ * rct2: 0x0068B932
+ * ax = x
+ * cx = y
+ * dl = zLow
+ * dh = zHigh
+ * ebp = clearFunc
+ * bl = bl
+ */
+std::unique_ptr MapCanConstructWithClearAt(
+ const CoordsXYRangedZ& pos, CLEAR_FUNC clearFunc, QuarterTile quarterTile, uint8_t flags, uint8_t crossingMode, bool isTree)
+{
+ int32_t northZ, eastZ, baseHeight, southZ, westZ, water_height;
+ northZ = eastZ = baseHeight = southZ = westZ = water_height = 0;
+ auto res = std::make_unique();
+ uint8_t slope = 0;
+
+ res->GroundFlags = ELEMENT_IS_ABOVE_GROUND;
+ bool canBuildCrossing = false;
+ if (map_is_edge(pos))
+ {
+ res->Error = GameActions::Status::InvalidParameters;
+ res->ErrorMessage = STR_OFF_EDGE_OF_MAP;
+ return res;
+ }
+
+ if (gCheatsDisableClearanceChecks)
+ {
+ return res;
+ }
+
+ TileElement* tileElement = map_get_first_element_at(pos);
+ if (tileElement == nullptr)
+ {
+ res->Error = GameActions::Status::Unknown;
+ res->ErrorMessage = STR_NONE;
+ return res;
+ }
+ do
+ {
+ if (tileElement->GetType() != TILE_ELEMENT_TYPE_SURFACE)
+ {
+ if (pos.baseZ < tileElement->GetClearanceZ() && pos.clearanceZ > tileElement->GetBaseZ()
+ && !(tileElement->IsGhost()))
+ {
+ if (tileElement->GetOccupiedQuadrants() & (quarterTile.GetBaseQuarterOccupied()))
+ {
+ if (MapLoc68BABCShouldContinue(
+ tileElement, pos, clearFunc, flags, res->Cost, crossingMode, canBuildCrossing))
+ {
+ continue;
+ }
+
+ if (tileElement != nullptr)
+ {
+ map_obstruction_set_error_text(tileElement, *res);
+ res->Error = GameActions::Status::NoClearance;
+ }
+ return res;
+ }
+ }
+ continue;
+ }
+ water_height = tileElement->AsSurface()->GetWaterHeight();
+ if (water_height && water_height > pos.baseZ && tileElement->GetBaseZ() < pos.clearanceZ)
+ {
+ res->GroundFlags |= ELEMENT_IS_UNDERWATER;
+ if (water_height < pos.clearanceZ)
+ {
+ bool returnError = true;
+ if (clearFunc != nullptr)
+ {
+ if (!clearFunc(&tileElement, pos, flags, &res->Cost))
+ {
+ returnError = false;
+ }
+ }
+ if (returnError)
+ {
+ if (tileElement != nullptr)
+ {
+ res->Error = GameActions::Status::NoClearance;
+ res->ErrorMessage = STR_CANNOT_BUILD_PARTLY_ABOVE_AND_PARTLY_BELOW_WATER;
+ }
+ return res;
+ }
+ }
+ }
+
+ if (gParkFlags & PARK_FLAGS_FORBID_HIGH_CONSTRUCTION && !isTree)
+ {
+ auto heightFromGround = pos.clearanceZ - tileElement->GetBaseZ();
+
+ if (heightFromGround > (18 * COORDS_Z_STEP))
+ {
+ res->Error = GameActions::Status::Disallowed;
+ res->ErrorMessage = STR_LOCAL_AUTHORITY_WONT_ALLOW_CONSTRUCTION_ABOVE_TREE_HEIGHT;
+ return res;
+ }
+ }
+
+ // Only allow building crossings directly on a flat surface tile.
+ if (tileElement->GetType() == TILE_ELEMENT_TYPE_SURFACE
+ && (tileElement->AsSurface()->GetSlope()) == TILE_ELEMENT_SLOPE_FLAT && tileElement->GetBaseZ() == pos.baseZ)
+ {
+ canBuildCrossing = true;
+ }
+
+ if (quarterTile.GetZQuarterOccupied() != 0b1111)
+ {
+ if (tileElement->GetBaseZ() >= pos.clearanceZ)
+ {
+ // loc_68BA81
+ res->GroundFlags |= ELEMENT_IS_UNDERGROUND;
+ res->GroundFlags &= ~ELEMENT_IS_ABOVE_GROUND;
+ }
+ else
+ {
+ northZ = tileElement->GetBaseZ();
+ eastZ = northZ;
+ southZ = northZ;
+ westZ = northZ;
+ slope = tileElement->AsSurface()->GetSlope();
+ if (slope & TILE_ELEMENT_SLOPE_N_CORNER_UP)
+ {
+ northZ += LAND_HEIGHT_STEP;
+ if (slope == (TILE_ELEMENT_SLOPE_S_CORNER_DN | TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT))
+ northZ += LAND_HEIGHT_STEP;
+ }
+ if (slope & TILE_ELEMENT_SLOPE_E_CORNER_UP)
+ {
+ eastZ += LAND_HEIGHT_STEP;
+ if (slope == (TILE_ELEMENT_SLOPE_W_CORNER_DN | TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT))
+ eastZ += LAND_HEIGHT_STEP;
+ }
+ if (slope & TILE_ELEMENT_SLOPE_S_CORNER_UP)
+ {
+ southZ += LAND_HEIGHT_STEP;
+ if (slope == (TILE_ELEMENT_SLOPE_N_CORNER_DN | TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT))
+ southZ += LAND_HEIGHT_STEP;
+ }
+ if (slope & TILE_ELEMENT_SLOPE_W_CORNER_UP)
+ {
+ westZ += LAND_HEIGHT_STEP;
+ if (slope == (TILE_ELEMENT_SLOPE_E_CORNER_DN | TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT))
+ westZ += LAND_HEIGHT_STEP;
+ }
+ baseHeight = pos.baseZ + (4 * COORDS_Z_STEP);
+ {
+ auto baseQuarter = quarterTile.GetBaseQuarterOccupied();
+ auto zQuarter = quarterTile.GetZQuarterOccupied();
+ if ((!(baseQuarter & 0b0001) || ((zQuarter & 0b0001 || pos.baseZ >= northZ) && baseHeight >= northZ))
+ && (!(baseQuarter & 0b0010) || ((zQuarter & 0b0010 || pos.baseZ >= eastZ) && baseHeight >= eastZ))
+ && (!(baseQuarter & 0b0100) || ((zQuarter & 0b0100 || pos.baseZ >= southZ) && baseHeight >= southZ))
+ && (!(baseQuarter & 0b1000) || ((zQuarter & 0b1000 || pos.baseZ >= westZ) && baseHeight >= westZ)))
+ {
+ continue;
+ }
+ }
+
+ if (MapLoc68BABCShouldContinue(tileElement, pos, clearFunc, flags, res->Cost, crossingMode, canBuildCrossing))
+ {
+ continue;
+ }
+
+ if (tileElement != nullptr)
+ {
+ map_obstruction_set_error_text(tileElement, *res);
+ res->Error = GameActions::Status::NoClearance;
+ }
+ return res;
+ }
+ }
+ } while (!(tileElement++)->IsLastForTile());
+ return res;
+}
+
+std::unique_ptr MapCanConstructAt(const CoordsXYRangedZ& pos, QuarterTile bl)
+{
+ return MapCanConstructWithClearAt(pos, nullptr, bl, 0);
+}
+
+/**
+ *
+ * rct2: 0x0068BB18
+ */
+void map_obstruction_set_error_text(TileElement* tileElement, GameActions::Result& res)
+{
+ Ride* ride;
+
+ res.ErrorMessage = STR_OBJECT_IN_THE_WAY;
+ switch (tileElement->GetType())
+ {
+ case TILE_ELEMENT_TYPE_SURFACE:
+ res.ErrorMessage = STR_RAISE_OR_LOWER_LAND_FIRST;
+ break;
+ case TILE_ELEMENT_TYPE_PATH:
+ res.ErrorMessage = STR_FOOTPATH_IN_THE_WAY;
+ break;
+ case TILE_ELEMENT_TYPE_TRACK:
+ ride = get_ride(tileElement->AsTrack()->GetRideIndex());
+ if (ride != nullptr)
+ {
+ res.ErrorMessage = STR_X_IN_THE_WAY;
+
+ Formatter ft(res.ErrorMessageArgs.data());
+ ride->FormatNameTo(ft);
+ }
+ break;
+ case TILE_ELEMENT_TYPE_SMALL_SCENERY:
+ {
+ auto* sceneryEntry = tileElement->AsSmallScenery()->GetEntry();
+ res.ErrorMessage = STR_X_IN_THE_WAY;
+ auto ft = Formatter(res.ErrorMessageArgs.data());
+ rct_string_id stringId = sceneryEntry != nullptr ? sceneryEntry->name : static_cast(STR_EMPTY);
+ ft.Add(stringId);
+ break;
+ }
+ case TILE_ELEMENT_TYPE_ENTRANCE:
+ switch (tileElement->AsEntrance()->GetEntranceType())
+ {
+ case ENTRANCE_TYPE_RIDE_ENTRANCE:
+ res.ErrorMessage = STR_RIDE_ENTRANCE_IN_THE_WAY;
+ break;
+ case ENTRANCE_TYPE_RIDE_EXIT:
+ res.ErrorMessage = STR_RIDE_EXIT_IN_THE_WAY;
+ break;
+ case ENTRANCE_TYPE_PARK_ENTRANCE:
+ res.ErrorMessage = STR_PARK_ENTRANCE_IN_THE_WAY;
+ break;
+ }
+ break;
+ case TILE_ELEMENT_TYPE_WALL:
+ {
+ auto* wallEntry = tileElement->AsWall()->GetEntry();
+ res.ErrorMessage = STR_X_IN_THE_WAY;
+ auto ft = Formatter(res.ErrorMessageArgs.data());
+ rct_string_id stringId = wallEntry != nullptr ? wallEntry->name : static_cast(STR_EMPTY);
+ ft.Add(stringId);
+ break;
+ }
+ case TILE_ELEMENT_TYPE_LARGE_SCENERY:
+ {
+ auto* sceneryEntry = tileElement->AsLargeScenery()->GetEntry();
+ res.ErrorMessage = STR_X_IN_THE_WAY;
+ auto ft = Formatter(res.ErrorMessageArgs.data());
+ rct_string_id stringId = sceneryEntry != nullptr ? sceneryEntry->name : static_cast(STR_EMPTY);
+ ft.Add(stringId);
+ break;
+ }
+ }
+}
diff --git a/src/openrct2/world/ConstructionClearance.h b/src/openrct2/world/ConstructionClearance.h
new file mode 100644
index 0000000000..9662e04e1a
--- /dev/null
+++ b/src/openrct2/world/ConstructionClearance.h
@@ -0,0 +1,25 @@
+/*****************************************************************************
+ * Copyright (c) 2014-2021 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.
+ *****************************************************************************/
+
+#pragma once
+
+#include "../actions/GameActionResult.h"
+
+using CLEAR_FUNC = int32_t (*)(TileElement** tile_element, const CoordsXY& coords, uint8_t flags, money32* price);
+
+int32_t map_place_non_scenery_clear_func(TileElement** tile_element, const CoordsXY& coords, uint8_t flags, money32* price);
+int32_t map_place_scenery_clear_func(TileElement** tile_element, const CoordsXY& coords, uint8_t flags, money32* price);
+
+[[nodiscard]] std::unique_ptr MapCanConstructWithClearAt(
+ const CoordsXYRangedZ& pos, CLEAR_FUNC clearFunc, QuarterTile quarterTile, uint8_t flags,
+ uint8_t crossingMode = CREATE_CROSSING_MODE_NONE, bool isTree = false);
+
+[[nodiscard]] std::unique_ptr MapCanConstructAt(const CoordsXYRangedZ& pos, QuarterTile bl);
+
+void map_obstruction_set_error_text(TileElement* tileElement, GameActions::Result& res);
diff --git a/src/openrct2/world/Map.cpp b/src/openrct2/world/Map.cpp
index 7185a6665f..b50ed72521 100644
--- a/src/openrct2/world/Map.cpp
+++ b/src/openrct2/world/Map.cpp
@@ -1264,292 +1264,6 @@ TileElement* tile_element_insert(const CoordsXYZ& loc, int32_t occupiedQuadrants
return insertedElement;
}
-/**
- *
- * rct2: 0x0068BB18
- */
-void map_obstruction_set_error_text(TileElement* tileElement, GameActions::Result& res)
-{
- Ride* ride;
-
- res.ErrorMessage = STR_OBJECT_IN_THE_WAY;
- switch (tileElement->GetType())
- {
- case TILE_ELEMENT_TYPE_SURFACE:
- res.ErrorMessage = STR_RAISE_OR_LOWER_LAND_FIRST;
- break;
- case TILE_ELEMENT_TYPE_PATH:
- res.ErrorMessage = STR_FOOTPATH_IN_THE_WAY;
- break;
- case TILE_ELEMENT_TYPE_TRACK:
- ride = get_ride(tileElement->AsTrack()->GetRideIndex());
- if (ride != nullptr)
- {
- res.ErrorMessage = STR_X_IN_THE_WAY;
-
- Formatter ft(res.ErrorMessageArgs.data());
- ride->FormatNameTo(ft);
- }
- break;
- case TILE_ELEMENT_TYPE_SMALL_SCENERY:
- {
- auto* sceneryEntry = tileElement->AsSmallScenery()->GetEntry();
- res.ErrorMessage = STR_X_IN_THE_WAY;
- auto ft = Formatter(res.ErrorMessageArgs.data());
- rct_string_id stringId = sceneryEntry != nullptr ? sceneryEntry->name : static_cast(STR_EMPTY);
- ft.Add(stringId);
- break;
- }
- case TILE_ELEMENT_TYPE_ENTRANCE:
- switch (tileElement->AsEntrance()->GetEntranceType())
- {
- case ENTRANCE_TYPE_RIDE_ENTRANCE:
- res.ErrorMessage = STR_RIDE_ENTRANCE_IN_THE_WAY;
- break;
- case ENTRANCE_TYPE_RIDE_EXIT:
- res.ErrorMessage = STR_RIDE_EXIT_IN_THE_WAY;
- break;
- case ENTRANCE_TYPE_PARK_ENTRANCE:
- res.ErrorMessage = STR_PARK_ENTRANCE_IN_THE_WAY;
- break;
- }
- break;
- case TILE_ELEMENT_TYPE_WALL:
- {
- auto* wallEntry = tileElement->AsWall()->GetEntry();
- res.ErrorMessage = STR_X_IN_THE_WAY;
- auto ft = Formatter(res.ErrorMessageArgs.data());
- rct_string_id stringId = wallEntry != nullptr ? wallEntry->name : static_cast(STR_EMPTY);
- ft.Add(stringId);
- break;
- }
- case TILE_ELEMENT_TYPE_LARGE_SCENERY:
- {
- auto* sceneryEntry = tileElement->AsLargeScenery()->GetEntry();
- res.ErrorMessage = STR_X_IN_THE_WAY;
- auto ft = Formatter(res.ErrorMessageArgs.data());
- rct_string_id stringId = sceneryEntry != nullptr ? sceneryEntry->name : static_cast(STR_EMPTY);
- ft.Add(stringId);
- break;
- }
- }
-}
-
-static bool MapLoc68BABCShouldContinue(
- TileElement* tileElement, const CoordsXYRangedZ& pos, CLEAR_FUNC clearFunc, uint8_t flags, money32& price,
- uint8_t crossingMode, bool canBuildCrossing)
-{
- if (clearFunc != nullptr)
- {
- if (!clearFunc(&tileElement, pos, flags, &price))
- {
- return true;
- }
- }
-
- // Crossing mode 1: building track over path
- if (crossingMode == 1 && canBuildCrossing && tileElement->GetType() == TILE_ELEMENT_TYPE_PATH
- && tileElement->GetBaseZ() == pos.baseZ && !tileElement->AsPath()->IsQueue() && !tileElement->AsPath()->IsSloped())
- {
- return true;
- }
- // Crossing mode 2: building path over track
- else if (
- crossingMode == 2 && canBuildCrossing && tileElement->GetType() == TILE_ELEMENT_TYPE_TRACK
- && tileElement->GetBaseZ() == pos.baseZ && tileElement->AsTrack()->GetTrackType() == TrackElemType::Flat)
- {
- auto ride = get_ride(tileElement->AsTrack()->GetRideIndex());
- if (ride != nullptr && ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_SUPPORTS_LEVEL_CROSSINGS))
- {
- return true;
- }
- }
-
- return false;
-}
-
-/**
- *
- * rct2: 0x0068B932
- * ax = x
- * cx = y
- * dl = zLow
- * dh = zHigh
- * ebp = clearFunc
- * bl = bl
- */
-std::unique_ptr MapCanConstructWithClearAt(
- const CoordsXYRangedZ& pos, CLEAR_FUNC clearFunc, QuarterTile quarterTile, uint8_t flags, uint8_t crossingMode, bool isTree)
-{
- int32_t northZ, eastZ, baseHeight, southZ, westZ, water_height;
- northZ = eastZ = baseHeight = southZ = westZ = water_height = 0;
- auto res = std::make_unique();
- uint8_t slope = 0;
-
- res->GroundFlags = ELEMENT_IS_ABOVE_GROUND;
- bool canBuildCrossing = false;
- if (map_is_edge(pos))
- {
- res->Error = GameActions::Status::InvalidParameters;
- res->ErrorMessage = STR_OFF_EDGE_OF_MAP;
- return res;
- }
-
- if (gCheatsDisableClearanceChecks)
- {
- return res;
- }
-
- TileElement* tileElement = map_get_first_element_at(pos);
- if (tileElement == nullptr)
- {
- res->Error = GameActions::Status::Unknown;
- res->ErrorMessage = STR_NONE;
- return res;
- }
- do
- {
- if (tileElement->GetType() != TILE_ELEMENT_TYPE_SURFACE)
- {
- if (pos.baseZ < tileElement->GetClearanceZ() && pos.clearanceZ > tileElement->GetBaseZ()
- && !(tileElement->IsGhost()))
- {
- if (tileElement->GetOccupiedQuadrants() & (quarterTile.GetBaseQuarterOccupied()))
- {
- if (MapLoc68BABCShouldContinue(
- tileElement, pos, clearFunc, flags, res->Cost, crossingMode, canBuildCrossing))
- {
- continue;
- }
-
- if (tileElement != nullptr)
- {
- map_obstruction_set_error_text(tileElement, *res);
- res->Error = GameActions::Status::NoClearance;
- }
- return res;
- }
- }
- continue;
- }
- water_height = tileElement->AsSurface()->GetWaterHeight();
- if (water_height && water_height > pos.baseZ && tileElement->GetBaseZ() < pos.clearanceZ)
- {
- res->GroundFlags |= ELEMENT_IS_UNDERWATER;
- if (water_height < pos.clearanceZ)
- {
- bool returnError = true;
- if (clearFunc != nullptr)
- {
- if (!clearFunc(&tileElement, pos, flags, &res->Cost))
- {
- returnError = false;
- }
- }
- if (returnError)
- {
- if (tileElement != nullptr)
- {
- res->Error = GameActions::Status::NoClearance;
- res->ErrorMessage = STR_CANNOT_BUILD_PARTLY_ABOVE_AND_PARTLY_BELOW_WATER;
- }
- return res;
- }
- }
- }
-
- if (gParkFlags & PARK_FLAGS_FORBID_HIGH_CONSTRUCTION && !isTree)
- {
- auto heightFromGround = pos.clearanceZ - tileElement->GetBaseZ();
-
- if (heightFromGround > (18 * COORDS_Z_STEP))
- {
- res->Error = GameActions::Status::Disallowed;
- res->ErrorMessage = STR_LOCAL_AUTHORITY_WONT_ALLOW_CONSTRUCTION_ABOVE_TREE_HEIGHT;
- return res;
- }
- }
-
- // Only allow building crossings directly on a flat surface tile.
- if (tileElement->GetType() == TILE_ELEMENT_TYPE_SURFACE
- && (tileElement->AsSurface()->GetSlope()) == TILE_ELEMENT_SLOPE_FLAT && tileElement->GetBaseZ() == pos.baseZ)
- {
- canBuildCrossing = true;
- }
-
- if (quarterTile.GetZQuarterOccupied() != 0b1111)
- {
- if (tileElement->GetBaseZ() >= pos.clearanceZ)
- {
- // loc_68BA81
- res->GroundFlags |= ELEMENT_IS_UNDERGROUND;
- res->GroundFlags &= ~ELEMENT_IS_ABOVE_GROUND;
- }
- else
- {
- northZ = tileElement->GetBaseZ();
- eastZ = northZ;
- southZ = northZ;
- westZ = northZ;
- slope = tileElement->AsSurface()->GetSlope();
- if (slope & TILE_ELEMENT_SLOPE_N_CORNER_UP)
- {
- northZ += LAND_HEIGHT_STEP;
- if (slope == (TILE_ELEMENT_SLOPE_S_CORNER_DN | TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT))
- northZ += LAND_HEIGHT_STEP;
- }
- if (slope & TILE_ELEMENT_SLOPE_E_CORNER_UP)
- {
- eastZ += LAND_HEIGHT_STEP;
- if (slope == (TILE_ELEMENT_SLOPE_W_CORNER_DN | TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT))
- eastZ += LAND_HEIGHT_STEP;
- }
- if (slope & TILE_ELEMENT_SLOPE_S_CORNER_UP)
- {
- southZ += LAND_HEIGHT_STEP;
- if (slope == (TILE_ELEMENT_SLOPE_N_CORNER_DN | TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT))
- southZ += LAND_HEIGHT_STEP;
- }
- if (slope & TILE_ELEMENT_SLOPE_W_CORNER_UP)
- {
- westZ += LAND_HEIGHT_STEP;
- if (slope == (TILE_ELEMENT_SLOPE_E_CORNER_DN | TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT))
- westZ += LAND_HEIGHT_STEP;
- }
- baseHeight = pos.baseZ + (4 * COORDS_Z_STEP);
- {
- auto baseQuarter = quarterTile.GetBaseQuarterOccupied();
- auto zQuarter = quarterTile.GetZQuarterOccupied();
- if ((!(baseQuarter & 0b0001) || ((zQuarter & 0b0001 || pos.baseZ >= northZ) && baseHeight >= northZ))
- && (!(baseQuarter & 0b0010) || ((zQuarter & 0b0010 || pos.baseZ >= eastZ) && baseHeight >= eastZ))
- && (!(baseQuarter & 0b0100) || ((zQuarter & 0b0100 || pos.baseZ >= southZ) && baseHeight >= southZ))
- && (!(baseQuarter & 0b1000) || ((zQuarter & 0b1000 || pos.baseZ >= westZ) && baseHeight >= westZ)))
- {
- continue;
- }
- }
-
- if (MapLoc68BABCShouldContinue(tileElement, pos, clearFunc, flags, res->Cost, crossingMode, canBuildCrossing))
- {
- continue;
- }
-
- if (tileElement != nullptr)
- {
- map_obstruction_set_error_text(tileElement, *res);
- res->Error = GameActions::Status::NoClearance;
- }
- return res;
- }
- }
- } while (!(tileElement++)->IsLastForTile());
- return res;
-}
-
-std::unique_ptr MapCanConstructAt(const CoordsXYRangedZ& pos, QuarterTile bl)
-{
- return MapCanConstructWithClearAt(pos, nullptr, bl, 0);
-}
/**
* Updates grass length, scenery age and jumping fountains.
*
diff --git a/src/openrct2/world/Map.h b/src/openrct2/world/Map.h
index d9f524c646..38654a35d4 100644
--- a/src/openrct2/world/Map.h
+++ b/src/openrct2/world/Map.h
@@ -233,21 +233,6 @@ template T* TileElementInsert(const CoordsXYZ& loc, int32_t occupied
return (element != nullptr) ? element->template as() : nullptr;
}
-namespace GameActions
-{
- class Result;
- class ConstructClearResult;
-} // namespace GameActions
-
-using CLEAR_FUNC = int32_t (*)(TileElement** tile_element, const CoordsXY& coords, uint8_t flags, money32* price);
-
-int32_t map_place_non_scenery_clear_func(TileElement** tile_element, const CoordsXY& coords, uint8_t flags, money32* price);
-int32_t map_place_scenery_clear_func(TileElement** tile_element, const CoordsXY& coords, uint8_t flags, money32* price);
-[[nodiscard]] std::unique_ptr MapCanConstructWithClearAt(
- const CoordsXYRangedZ& pos, CLEAR_FUNC clearFunc, QuarterTile quarterTile, uint8_t flags,
- uint8_t crossingMode = CREATE_CROSSING_MODE_NONE, bool isTree = false);
-[[nodiscard]] std::unique_ptr MapCanConstructAt(const CoordsXYRangedZ& pos, QuarterTile bl);
-
struct tile_element_iterator
{
int32_t x;
@@ -305,7 +290,6 @@ TileElement* map_get_track_element_at_from_ride(const CoordsXYZ& trackPos, ride_
TileElement* map_get_track_element_at_with_direction_from_ride(const CoordsXYZD& trackPos, ride_id_t rideIndex);
bool map_is_location_at_edge(const CoordsXY& loc);
-void map_obstruction_set_error_text(TileElement* tileElement, GameActions::Result& res);
uint16_t check_max_allowable_land_rights_for_tile(const CoordsXYZ& tileMapPos);
diff --git a/src/openrct2/world/SmallScenery.cpp b/src/openrct2/world/SmallScenery.cpp
index 8e01331916..5abe1a2967 100644
--- a/src/openrct2/world/SmallScenery.cpp
+++ b/src/openrct2/world/SmallScenery.cpp
@@ -24,58 +24,6 @@
#include "Scenery.h"
#include "Surface.h"
-static int32_t map_place_clear_func(
- TileElement** tile_element, const CoordsXY& coords, uint8_t flags, money32* price, bool is_scenery)
-{
- if ((*tile_element)->GetType() != TILE_ELEMENT_TYPE_SMALL_SCENERY)
- return 1;
-
- if (is_scenery && !(flags & GAME_COMMAND_FLAG_PATH_SCENERY))
- return 1;
-
- auto* scenery = (*tile_element)->AsSmallScenery()->GetEntry();
-
- if (gParkFlags & PARK_FLAGS_FORBID_TREE_REMOVAL)
- {
- if (scenery != nullptr && scenery->HasFlag(SMALL_SCENERY_FLAG_IS_TREE))
- return 1;
- }
-
- if (!(gParkFlags & PARK_FLAGS_NO_MONEY) && scenery != nullptr)
- *price += scenery->removal_price * 10;
-
- if (flags & GAME_COMMAND_FLAG_GHOST)
- return 0;
-
- if (!(flags & GAME_COMMAND_FLAG_APPLY))
- return 0;
-
- map_invalidate_tile({ coords, (*tile_element)->GetBaseZ(), (*tile_element)->GetClearanceZ() });
-
- tile_element_remove(*tile_element);
-
- (*tile_element)--;
- return 0;
-}
-
-/**
- *
- * rct2: 0x006E0D6E, 0x006B8D88
- */
-int32_t map_place_scenery_clear_func(TileElement** tile_element, const CoordsXY& coords, uint8_t flags, money32* price)
-{
- return map_place_clear_func(tile_element, coords, flags, price, /*is_scenery=*/true);
-}
-
-/**
- *
- * rct2: 0x006C5A4F, 0x006CDE57, 0x006A6733, 0x0066637E
- */
-int32_t map_place_non_scenery_clear_func(TileElement** tile_element, const CoordsXY& coords, uint8_t flags, money32* price)
-{
- return map_place_clear_func(tile_element, coords, flags, price, /*is_scenery=*/false);
-}
-
uint8_t SmallSceneryElement::GetSceneryQuadrant() const
{
return (this->type & TILE_ELEMENT_QUADRANT_MASK) >> 6;