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:
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "../management/Finance.h"
|
||||
#include "../ride/RideData.h"
|
||||
#include "../ride/TrackData.h"
|
||||
#include "../world/ConstructionClearance.h"
|
||||
|
||||
using namespace OpenRCT2::TrackMetaData;
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include "../OpenRCT2.h"
|
||||
#include "../management/Finance.h"
|
||||
#include "../world/ConstructionClearance.h"
|
||||
#include "../world/Park.h"
|
||||
#include "../world/Surface.h"
|
||||
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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"
|
||||
|
||||
359
src/openrct2/world/ConstructionClearance.cpp
Normal file
359
src/openrct2/world/ConstructionClearance.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
25
src/openrct2/world/ConstructionClearance.h
Normal file
25
src/openrct2/world/ConstructionClearance.h
Normal 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);
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user