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;