1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2025-12-11 10:02:27 +01:00

Refactor out construction clearance into a new compilation unit

This commit is contained in:
ζeh Matt
2021-10-20 21:47:56 +03:00
parent 60422960a3
commit 7a30169c28
19 changed files with 399 additions and 354 deletions

View File

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

View File

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

View File

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

View File

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

View File

@@ -11,6 +11,7 @@
#include "../management/Finance.h"
#include "../ride/RideData.h"
#include "../ride/TrackData.h"
#include "../world/ConstructionClearance.h"
using namespace OpenRCT2::TrackMetaData;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -11,6 +11,7 @@
#include "../OpenRCT2.h"
#include "../management/Finance.h"
#include "../world/ConstructionClearance.h"
#include "../world/Park.h"
#include "../world/Surface.h"

View File

@@ -466,6 +466,7 @@
<ClInclude Include="world\Balloon.h" />
<ClInclude Include="world\Banner.h" />
<ClInclude Include="world\Climate.h" />
<ClInclude Include="world\ConstructionClearance.h" />
<ClInclude Include="world\Duck.h" />
<ClInclude Include="world\Entity.h" />
<ClInclude Include="world\EntityList.h" />
@@ -911,6 +912,7 @@
<ClCompile Include="world\Balloon.cpp" />
<ClCompile Include="world\Banner.cpp" />
<ClCompile Include="world\Climate.cpp" />
<ClCompile Include="world\ConstructionClearance.cpp" />
<ClCompile Include="world\Duck.cpp" />
<ClCompile Include="world\Entity.cpp" />
<ClCompile Include="world\EntityTweener.cpp" />

View File

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

View File

@@ -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<GameActions::ConstructClearResult> 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<GameActions::ConstructClearResult>();
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<GameActions::ConstructClearResult> 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<rct_string_id>(STR_EMPTY);
ft.Add<rct_string_id>(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<rct_string_id>(STR_EMPTY);
ft.Add<rct_string_id>(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<rct_string_id>(STR_EMPTY);
ft.Add<rct_string_id>(stringId);
break;
}
}
}

View File

@@ -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<GameActions::ConstructClearResult> 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<GameActions::ConstructClearResult> MapCanConstructAt(const CoordsXYRangedZ& pos, QuarterTile bl);
void map_obstruction_set_error_text(TileElement* tileElement, GameActions::Result& res);

View File

@@ -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<rct_string_id>(STR_EMPTY);
ft.Add<rct_string_id>(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<rct_string_id>(STR_EMPTY);
ft.Add<rct_string_id>(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<rct_string_id>(STR_EMPTY);
ft.Add<rct_string_id>(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<GameActions::ConstructClearResult> 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<GameActions::ConstructClearResult>();
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<GameActions::ConstructClearResult> MapCanConstructAt(const CoordsXYRangedZ& pos, QuarterTile bl)
{
return MapCanConstructWithClearAt(pos, nullptr, bl, 0);
}
/**
* Updates grass length, scenery age and jumping fountains.
*

View File

@@ -233,21 +233,6 @@ template<typename T> T* TileElementInsert(const CoordsXYZ& loc, int32_t occupied
return (element != nullptr) ? element->template as<T>() : 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<GameActions::ConstructClearResult> 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<GameActions::ConstructClearResult> 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);

View File

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