Merge pull request #8689 from duncanspumpkin/land_set_heigh_gc

Move Land Set Height into GameAction Framework
This commit is contained in:
Duncan 2019-02-08 15:53:47 +00:00 committed by GitHub
commit f32531c911
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 452 additions and 273 deletions

View File

@ -21,10 +21,11 @@
/* End PBXAggregateTarget section */
/* Begin PBXBuildFile section */
2A5354E922099C4F00A5440F /* Network.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2A5354E822099C4F00A5440F /* Network.cpp */; };
2AA050322209A8E300D3A922 /* StaffSetCostumeAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2AA050302209A8E300D3A922 /* StaffSetCostumeAction.hpp */; };
2AA050332209A8E300D3A922 /* StaffSetOrdersAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2AA050312209A8E300D3A922 /* StaffSetOrdersAction.hpp */; };
2AAFD800220DD3D2002461A4 /* LandSetHeightAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2AAFD7FF220DD3D2002461A4 /* LandSetHeightAction.hpp */; };
2AF7893D220B253E0072754A /* RideSetAppearanceAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2AF7893C220B253E0072754A /* RideSetAppearanceAction.hpp */; };
2A5354E922099C4F00A5440F /* Network.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2A5354E822099C4F00A5440F /* Network.cpp */; };
4C29DEB3218C6AE500E8707F /* RCT12.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C29DEB2218C6AE500E8707F /* RCT12.cpp */; };
4C358E5221C445F700ADE6BC /* ReplayManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C358E5021C445F700ADE6BC /* ReplayManager.cpp */; };
4C3B4236205914F7000C5BB7 /* InGameConsole.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C3B4234205914F7000C5BB7 /* InGameConsole.cpp */; };
@ -616,12 +617,13 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
2A5354E822099C4F00A5440F /* Network.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Network.cpp; sourceTree = "<group>"; };
2A5354EA22099C7200A5440F /* CircularBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CircularBuffer.h; sourceTree = "<group>"; };
2A5354EB22099D7700A5440F /* SignSetStyleAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SignSetStyleAction.hpp; sourceTree = "<group>"; };
2AA050302209A8E300D3A922 /* StaffSetCostumeAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = StaffSetCostumeAction.hpp; sourceTree = "<group>"; };
2AA050312209A8E300D3A922 /* StaffSetOrdersAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = StaffSetOrdersAction.hpp; sourceTree = "<group>"; };
2AAFD7FF220DD3D2002461A4 /* LandSetHeightAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = LandSetHeightAction.hpp; sourceTree = "<group>"; };
2AF7893C220B253E0072754A /* RideSetAppearanceAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = RideSetAppearanceAction.hpp; sourceTree = "<group>"; };
2A5354E822099C4F00A5440F /* Network.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Network.cpp; sourceTree = "<group>"; };
2A5354EA22099C7200A5440F /* CircularBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CircularBuffer.h; sourceTree = "<group>"; };
4C04D69F2056AA9600F82EBA /* linenoise.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = linenoise.hpp; sourceTree = "<group>"; };
4C1A53EC205FD19F000F8EF5 /* SceneryObject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SceneryObject.cpp; sourceTree = "<group>"; };
4C29DEB2218C6AE500E8707F /* RCT12.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RCT12.cpp; sourceTree = "<group>"; };
@ -2000,6 +2002,7 @@
C6352B871F477032006CCEE3 /* actions */ = {
isa = PBXGroup;
children = (
2AAFD7FF220DD3D2002461A4 /* LandSetHeightAction.hpp */,
2AF7893C220B253E0072754A /* RideSetAppearanceAction.hpp */,
2A5354EB22099D7700A5440F /* SignSetStyleAction.hpp */,
2AA050302209A8E300D3A922 /* StaffSetCostumeAction.hpp */,
@ -3323,6 +3326,7 @@
2AF7893D220B253E0072754A /* RideSetAppearanceAction.hpp in Headers */,
C67B28162002D67A00109C93 /* Window.h in Headers */,
C6352B961F477032006CCEE3 /* RideSetStatus.hpp in Headers */,
2AAFD800220DD3D2002461A4 /* LandSetHeightAction.hpp in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@ -1455,7 +1455,7 @@ void game_load_or_quit_no_save_prompt()
GAME_COMMAND_POINTER* new_game_command_table[GAME_COMMAND_COUNT] = {
nullptr,
game_command_set_land_height,
nullptr,
game_pause_toggle,
game_command_place_track,
game_command_remove_track,

View File

@ -19,7 +19,7 @@ struct rct_s6_data;
enum GAME_COMMAND
{
GAME_COMMAND_SET_RIDE_APPEARANCE, // GA
GAME_COMMAND_SET_LAND_HEIGHT,
GAME_COMMAND_SET_LAND_HEIGHT, // GA
GAME_COMMAND_TOGGLE_PAUSE,
GAME_COMMAND_PLACE_TRACK,
GAME_COMMAND_REMOVE_TRACK,

View File

@ -13,6 +13,7 @@
#include "FootpathRemoveAction.hpp"
#include "GameAction.h"
#include "GuestSetNameAction.hpp"
#include "LandSetHeightAction.hpp"
#include "LargeSceneryRemoveAction.hpp"
#include "MazeSetTrackAction.hpp"
#include "ParkMarketingAction.hpp"
@ -68,6 +69,7 @@ namespace GameActions
Register<WallRemoveAction>();
Register<SmallSceneryRemoveAction>();
Register<LargeSceneryRemoveAction>();
Register<LandSetHeightAction>();
Register<ClearAction>();
}
} // namespace GameActions

View File

@ -0,0 +1,385 @@
/*****************************************************************************
* Copyright (c) 2014-2018 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 "../Context.h"
#include "../OpenRCT2.h"
#include "../interface/Window.h"
#include "../localisation/Localisation.h"
#include "../localisation/StringIds.h"
#include "../management/Finance.h"
#include "../ride/RideData.h"
#include "../windows/Intent.h"
#include "../world/Park.h"
#include "../world/Scenery.h"
#include "../world/Sprite.h"
#include "../world/Surface.h"
#include "GameAction.h"
DEFINE_GAME_ACTION(LandSetHeightAction, GAME_COMMAND_SET_LAND_HEIGHT, GameActionResult)
{
private:
CoordsXY _coords;
uint8_t _height;
uint8_t _style;
public:
LandSetHeightAction()
{
}
LandSetHeightAction(CoordsXY coords, uint8_t height, uint8_t style)
: _coords(coords)
, _height(height)
, _style(style)
{
}
uint16_t GetActionFlags() const override
{
return GameAction::GetActionFlags() | GA_FLAGS::EDITOR_ONLY;
}
void Serialise(DataSerialiser & stream) override
{
GameAction::Serialise(stream);
stream << DS_TAG(_coords) << DS_TAG(_height) << DS_TAG(_style);
}
GameActionResult::Ptr Query() const override
{
if (gParkFlags & PARK_FLAGS_FORBID_LANDSCAPE_CHANGES)
{
return std::make_unique<GameActionResult>(GA_ERROR::DISALLOWED, STR_FORBIDDEN_BY_THE_LOCAL_AUTHORITY);
}
rct_string_id errorTitle = CheckParameters();
if (errorTitle != STR_NONE)
{
return std::make_unique<GameActionResult>(GA_ERROR::DISALLOWED, errorTitle);
}
if (!(gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && !gCheatsSandboxMode)
{
if (!map_is_location_in_park(_coords))
{
return std::make_unique<GameActionResult>(GA_ERROR::DISALLOWED, STR_LAND_NOT_OWNED_BY_PARK);
}
}
money32 cost{};
if (!gCheatsDisableClearanceChecks)
{
if (gParkFlags & PARK_FLAGS_FORBID_TREE_REMOVAL)
{
// Check for obstructing large trees
TileElement* tileElement = CheckTallTreeObstructions();
if (tileElement != nullptr)
{
map_obstruction_set_error_text(tileElement);
return std::make_unique<GameActionResult>(GA_ERROR::DISALLOWED, gGameCommandErrorText);
}
}
cost = GetSmallSceneryRemovalCost();
}
// Check for ride support limits
if (!gCheatsDisableSupportLimits)
{
errorTitle = CheckRideSupports();
if (errorTitle != STR_NONE)
{
return std::make_unique<GameActionResult>(GA_ERROR::DISALLOWED, errorTitle);
}
}
TileElement* surfaceElement = map_get_surface_element_at(_coords);
TileElement* tileElement = CheckFloatingStructures(surfaceElement, _height);
if (tileElement != nullptr)
{
map_obstruction_set_error_text(tileElement);
return std::make_unique<GameActionResult>(GA_ERROR::DISALLOWED, gGameCommandErrorText);
}
if (!gCheatsDisableClearanceChecks)
{
uint8_t zCorner = _height;
if (_style & TILE_ELEMENT_SURFACE_RAISED_CORNERS_MASK)
{
zCorner += 2;
if (_style & TILE_ELEMENT_SURFACE_DIAGONAL_FLAG)
{
zCorner += 2;
}
}
if (!map_can_construct_with_clear_at(
_coords.x, _coords.y, _height, zCorner, &map_set_land_height_clear_func, 0xF, 0, nullptr,
CREATE_CROSSING_MODE_NONE))
{
return std::make_unique<GameActionResult>(GA_ERROR::DISALLOWED, gGameCommandErrorText);
}
tileElement = CheckUnremovableObstructions(surfaceElement, zCorner);
if (tileElement != nullptr)
{
map_obstruction_set_error_text(tileElement);
return std::make_unique<GameActionResult>(GA_ERROR::DISALLOWED, gGameCommandErrorText);
}
}
auto res = std::make_unique<GameActionResult>();
res->Cost = cost + GetSurfaceHeightChangeCost(surfaceElement);
res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING;
return res;
}
GameActionResult::Ptr Execute() const override
{
money32 cost = MONEY(0, 0);
auto surfaceHeight = tile_element_height(_coords.x, _coords.y) & 0xFFFF;
footpath_remove_litter(_coords.x, _coords.y, surfaceHeight);
if (!gCheatsDisableClearanceChecks)
{
wall_remove_at(_coords.x, _coords.y, _height * 8 - 16, _height * 8 + 32);
cost += GetSmallSceneryRemovalCost();
SmallSceneryRemoval();
}
TileElement* surfaceElement = map_get_surface_element_at(_coords);
cost += GetSurfaceHeightChangeCost(surfaceElement);
SetSurfaceHeight(surfaceElement);
LocationXYZ16 coord;
coord.x = _coords.x + 16;
coord.y = _coords.y + 16;
coord.z = surfaceHeight;
network_set_player_last_action_coord(network_get_player_index(game_command_playerid), coord);
auto res = std::make_unique<GameActionResult>();
res->Position = { _coords.x + 16, _coords.y + 16, surfaceHeight };
res->Cost = cost;
res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING;
return res;
}
private:
rct_string_id CheckParameters() const
{
if (_coords.x > gMapSizeMaxXY || _coords.y > gMapSizeMaxXY)
{
return STR_OFF_EDGE_OF_MAP;
}
if (_height < MINIMUM_LAND_HEIGHT)
{
return STR_TOO_LOW;
}
// Divide by 2 and subtract 7 to get the in-game units.
if (_height > MAXIMUM_LAND_HEIGHT)
{
return STR_TOO_HIGH;
}
else if (_height > MAXIMUM_LAND_HEIGHT - 2 && (_style & TILE_ELEMENT_SURFACE_SLOPE_MASK) != 0)
{
return STR_TOO_HIGH;
}
if (_height == MAXIMUM_LAND_HEIGHT - 2 && (_style & TILE_ELEMENT_SURFACE_DIAGONAL_FLAG))
{
return STR_TOO_HIGH;
}
return STR_NONE;
}
TileElement* CheckTallTreeObstructions() const
{
TileElement* tileElement = map_get_first_element_at(_coords.x / 32, _coords.y / 32);
do
{
if (tileElement->GetType() != TILE_ELEMENT_TYPE_SMALL_SCENERY)
continue;
if (_height > tileElement->clearance_height)
continue;
if (_height + 4 < tileElement->base_height)
continue;
rct_scenery_entry* sceneryEntry = tileElement->AsSmallScenery()->GetEntry();
if (sceneryEntry->small_scenery.height > 64)
{
return tileElement;
}
} while (!(tileElement++)->IsLastForTile());
return nullptr;
}
money32 GetSmallSceneryRemovalCost() const
{
money32 cost{ 0 };
TileElement* tileElement = map_get_first_element_at(_coords.x / 32, _coords.y / 32);
do
{
if (tileElement->GetType() != TILE_ELEMENT_TYPE_SMALL_SCENERY)
continue;
if (_height > tileElement->clearance_height)
continue;
if (_height + 4 < tileElement->base_height)
continue;
rct_scenery_entry* sceneryEntry = tileElement->AsSmallScenery()->GetEntry();
cost += MONEY(sceneryEntry->small_scenery.removal_price, 0);
} while (!(tileElement++)->IsLastForTile());
return cost;
}
void SmallSceneryRemoval() const
{
TileElement* tileElement = map_get_first_element_at(_coords.x / 32, _coords.y / 32);
do
{
if (tileElement->GetType() != TILE_ELEMENT_TYPE_SMALL_SCENERY)
continue;
if (_height > tileElement->clearance_height)
continue;
if (_height + 4 < tileElement->base_height)
continue;
tile_element_remove(tileElement--);
} while (!(tileElement++)->IsLastForTile());
}
rct_string_id CheckRideSupports() const
{
TileElement* tileElement = map_get_first_element_at(_coords.x / 32, _coords.y / 32);
do
{
if (tileElement->GetType() == TILE_ELEMENT_TYPE_TRACK)
{
ride_id_t rideIndex = tileElement->AsTrack()->GetRideIndex();
Ride* ride = get_ride(rideIndex);
if (ride != nullptr)
{
rct_ride_entry* rideEntry = get_ride_entry_by_ride(ride);
if (rideEntry != nullptr)
{
int32_t maxHeight = rideEntry->max_height;
if (maxHeight == 0)
{
maxHeight = RideData5[get_ride(rideIndex)->type].max_height;
}
int32_t zDelta = tileElement->clearance_height - _height;
if (zDelta >= 0 && zDelta / 2 > maxHeight)
{
return STR_SUPPORTS_CANT_BE_EXTENDED;
}
}
}
}
} while (!(tileElement++)->IsLastForTile());
return STR_NONE;
}
TileElement* CheckFloatingStructures(TileElement * surfaceElement, uint8_t zCorner) const
{
if (surfaceElement->AsSurface()->HasTrackThatNeedsWater())
{
uint32_t waterHeight = surfaceElement->AsSurface()->GetWaterHeight();
if (waterHeight != 0)
{
if (_style & TILE_ELEMENT_SURFACE_SLOPE_MASK)
{
zCorner += 2;
if (_style & TILE_ELEMENT_SURFACE_DIAGONAL_FLAG)
{
zCorner += 2;
}
}
if (zCorner > waterHeight * 2 - 2)
{
return ++surfaceElement;
}
}
}
return nullptr;
}
TileElement* CheckUnremovableObstructions(TileElement * surfaceElement, uint8_t zCorner) const
{
TileElement* tileElement = map_get_first_element_at(_coords.x / 32, _coords.y / 32);
do
{
int32_t elementType = tileElement->GetType();
// Wall's and Small Scenery are removed and therefore do not need checked
if (elementType == TILE_ELEMENT_TYPE_WALL)
continue;
if (elementType == TILE_ELEMENT_TYPE_SMALL_SCENERY)
continue;
if (tileElement->IsGhost())
continue;
if (tileElement == surfaceElement)
continue;
if (tileElement > surfaceElement)
{
if (zCorner > tileElement->base_height)
{
return tileElement;
}
continue;
}
if (_height < tileElement->clearance_height)
{
return tileElement;
}
} while (!(tileElement++)->IsLastForTile());
return nullptr;
}
money32 GetSurfaceHeightChangeCost(TileElement * surfaceElement) const
{
money32 cost{ 0 };
for (int32_t i = 0; i < 4; i += 1)
{
int32_t cornerHeight = tile_element_get_corner_height(surfaceElement, i);
cornerHeight -= map_get_corner_height(_height, _style & TILE_ELEMENT_SURFACE_SLOPE_MASK, i);
cost += MONEY(abs(cornerHeight) * 5 / 2, 0);
}
return cost;
}
void SetSurfaceHeight(TileElement * surfaceElement) const
{
surfaceElement->base_height = _height;
surfaceElement->clearance_height = _height;
surfaceElement->AsSurface()->SetSlope(_style);
int32_t waterHeight = surfaceElement->AsSurface()->GetWaterHeight();
if (waterHeight != 0 && waterHeight <= _height / 2)
{
surfaceElement->AsSurface()->SetWaterHeight(0);
}
map_invalidate_tile_full(_coords.x, _coords.y);
}
/**
*
* rct2: 0x00663CB9
*/
static int32_t map_set_land_height_clear_func(
TileElement * *tile_element, [[maybe_unused]] int32_t x, [[maybe_unused]] int32_t y, [[maybe_unused]] uint8_t flags,
[[maybe_unused]] money32 * price)
{
if ((*tile_element)->GetType() == TILE_ELEMENT_TYPE_SURFACE)
return 0;
if ((*tile_element)->GetType() == TILE_ELEMENT_TYPE_SMALL_SCENERY)
return 0;
return 1;
}
};

View File

@ -321,3 +321,25 @@ template<> struct DataSerializerTraits<MapRange>
stream->Write(coords, strlen(coords));
}
};
template<> struct DataSerializerTraits<CoordsXY>
{
static void encode(IStream* stream, const CoordsXY& coords)
{
stream->WriteValue(ByteSwapBE(coords.x));
stream->WriteValue(ByteSwapBE(coords.y));
}
static void decode(IStream* stream, CoordsXY& coords)
{
auto x = ByteSwapBE(stream->ReadValue<int16_t>());
auto y = ByteSwapBE(stream->ReadValue<int16_t>());
coords = CoordsXY(x, y);
}
static void log(IStream* stream, const CoordsXY& coords)
{
char msg[128] = {};
snprintf(msg, sizeof(msg), "CoordsXY(x = %d, y = %d)", coords.x, coords.y);
stream->Write(msg, strlen(msg));
}
};

View File

@ -30,7 +30,7 @@
// This string specifies which version of network stream current build uses.
// It is used for making sure only compatible builds get connected, even within
// single OpenRCT2 version.
#define NETWORK_STREAM_VERSION "31"
#define NETWORK_STREAM_VERSION "32"
#define NETWORK_STREAM_ID OPENRCT2_VERSION "-" NETWORK_STREAM_VERSION
static rct_peep* _pickup_peep = nullptr;

View File

@ -15,6 +15,7 @@
#include "../Input.h"
#include "../OpenRCT2.h"
#include "../actions/FootpathRemoveAction.hpp"
#include "../actions/LandSetHeightAction.hpp"
#include "../actions/LargeSceneryRemoveAction.hpp"
#include "../actions/SmallSceneryRemoveAction.hpp"
#include "../actions/WallRemoveAction.hpp"
@ -1064,24 +1065,7 @@ static constexpr const uint8_t tile_element_lower_styles[9][32] = {
0x29, 0x29, 0x00, 0x01, 0x00, 0x01, 0x00, 0x07, 0x29, 0x29, 0x08, 0x09, 0x08, 0x09, 0x0E, 0x09 }, // MAP_SELECT_TYPE_EDGE_3
};
/**
*
* rct2: 0x00663CB9
*/
static int32_t map_set_land_height_clear_func(
TileElement** tile_element, [[maybe_unused]] int32_t x, [[maybe_unused]] int32_t y, [[maybe_unused]] uint8_t flags,
[[maybe_unused]] money32* price)
{
if ((*tile_element)->GetType() == TILE_ELEMENT_TYPE_SURFACE)
return 0;
if ((*tile_element)->GetType() == TILE_ELEMENT_TYPE_SMALL_SCENERY)
return 0;
return 1;
}
static int32_t map_get_corner_height(int32_t z, int32_t slope, int32_t direction)
int32_t map_get_corner_height(int32_t z, int32_t slope, int32_t direction)
{
switch (direction)
{
@ -1129,248 +1113,13 @@ static int32_t map_get_corner_height(int32_t z, int32_t slope, int32_t direction
return z;
}
static int32_t tile_element_get_corner_height(const TileElement* tileElement, int32_t direction)
int32_t tile_element_get_corner_height(const TileElement* tileElement, int32_t direction)
{
int32_t z = tileElement->base_height;
int32_t slope = tileElement->AsSurface()->GetSlope();
return map_get_corner_height(z, slope, direction);
}
static money32 map_set_land_height(int32_t flags, int32_t x, int32_t y, int32_t height, int32_t style)
{
TileElement* tileElement;
if (game_is_paused() && !gCheatsBuildInPauseMode)
{
gGameCommandErrorText = STR_CONSTRUCTION_NOT_POSSIBLE_WHILE_GAME_IS_PAUSED;
return MONEY32_UNDEFINED;
}
if (!(gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && !gCheatsSandboxMode)
{
if (gParkFlags & PARK_FLAGS_FORBID_LANDSCAPE_CHANGES)
{
gGameCommandErrorText = STR_FORBIDDEN_BY_THE_LOCAL_AUTHORITY;
return MONEY32_UNDEFINED;
}
}
if (x > gMapSizeMaxXY || y > gMapSizeMaxXY)
{
gGameCommandErrorText = STR_OFF_EDGE_OF_MAP;
return MONEY32_UNDEFINED;
}
if (height < MINIMUM_LAND_HEIGHT)
{
gGameCommandErrorText = STR_TOO_LOW;
return MONEY32_UNDEFINED;
}
// Divide by 2 and subtract 7 to get the in-game units.
if (height > MAXIMUM_LAND_HEIGHT)
{
gGameCommandErrorText = STR_TOO_HIGH;
return MONEY32_UNDEFINED;
}
else if (height > MAXIMUM_LAND_HEIGHT - 2 && (style & 0x1F) != 0)
{
gGameCommandErrorText = STR_TOO_HIGH;
return MONEY32_UNDEFINED;
}
if (height == MAXIMUM_LAND_HEIGHT - 2 && (style & 0x10))
{
gGameCommandErrorText = STR_TOO_HIGH;
return MONEY32_UNDEFINED;
}
if (!(gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && !gCheatsSandboxMode)
{
if (!map_is_location_in_park({ x, y }))
{
return MONEY32_UNDEFINED;
}
}
money32 cost = MONEY(0, 0);
if (flags & GAME_COMMAND_FLAG_APPLY)
{
footpath_remove_litter(x, y, tile_element_height(x, y));
if (!gCheatsDisableClearanceChecks)
wall_remove_at(x, y, height * 8 - 16, height * 8 + 32);
}
if (!gCheatsDisableClearanceChecks)
{
// Check for obstructing scenery
tileElement = map_get_first_element_at(x / 32, y / 32);
do
{
if (tileElement->GetType() != TILE_ELEMENT_TYPE_SMALL_SCENERY)
continue;
if (height > tileElement->clearance_height)
continue;
if (height + 4 < tileElement->base_height)
continue;
rct_scenery_entry* sceneryEntry = tileElement->AsSmallScenery()->GetEntry();
if (sceneryEntry->small_scenery.height > 64 && gParkFlags & PARK_FLAGS_FORBID_TREE_REMOVAL)
{
map_obstruction_set_error_text(tileElement);
return MONEY32_UNDEFINED;
}
cost += MONEY(sceneryEntry->small_scenery.removal_price, 0);
if (flags & GAME_COMMAND_FLAG_APPLY)
tile_element_remove(tileElement--);
} while (!(tileElement++)->IsLastForTile());
}
// Check for ride support limits
if (!gCheatsDisableSupportLimits)
{
tileElement = map_get_first_element_at(x / 32, y / 32);
do
{
if (tileElement->GetType() == TILE_ELEMENT_TYPE_TRACK)
{
ride_id_t rideIndex = tileElement->AsTrack()->GetRideIndex();
Ride* ride = get_ride(rideIndex);
if (ride != nullptr)
{
rct_ride_entry* rideEntry = get_ride_entry_by_ride(ride);
if (rideEntry != nullptr)
{
int32_t maxHeight = rideEntry->max_height;
if (maxHeight == 0)
{
maxHeight = RideData5[get_ride(rideIndex)->type].max_height;
}
int32_t zDelta = tileElement->clearance_height - height;
if (zDelta >= 0 && zDelta / 2 > maxHeight)
{
gGameCommandErrorText = STR_SUPPORTS_CANT_BE_EXTENDED;
return MONEY32_UNDEFINED;
}
}
}
}
} while (!(tileElement++)->IsLastForTile());
}
uint8_t zCorner = height; // z position of highest corner of tile
TileElement* surfaceElement = map_get_surface_element_at({ x, y });
if (surfaceElement->AsSurface()->HasTrackThatNeedsWater())
{
uint32_t waterHeight = surfaceElement->AsSurface()->GetWaterHeight();
if (waterHeight != 0)
{
if (style & 0x1F)
{
zCorner += 2;
if (style & 0x10)
{
zCorner += 2;
}
}
if (zCorner > waterHeight * 2 - 2)
{
surfaceElement++;
map_obstruction_set_error_text(surfaceElement);
return MONEY32_UNDEFINED;
}
}
}
zCorner = height;
if (style & 0xF)
{
zCorner += 2;
if (style & 0x10)
{
zCorner += 2;
}
}
if (!gCheatsDisableClearanceChecks)
{
if (!map_can_construct_with_clear_at(
x, y, height, zCorner, &map_set_land_height_clear_func, 0xF, 0, nullptr, CREATE_CROSSING_MODE_NONE))
{
return MONEY32_UNDEFINED;
}
}
if (!gCheatsDisableClearanceChecks)
{
tileElement = map_get_first_element_at(x / 32, y / 32);
do
{
int32_t elementType = tileElement->GetType();
if (elementType == TILE_ELEMENT_TYPE_WALL)
continue;
if (elementType == TILE_ELEMENT_TYPE_SMALL_SCENERY)
continue;
if (tileElement->flags & 0x10)
continue;
if (tileElement == surfaceElement)
continue;
if (tileElement > surfaceElement)
{
if (zCorner > tileElement->base_height)
{
map_obstruction_set_error_text(tileElement);
return MONEY32_UNDEFINED;
}
continue;
}
if (height < tileElement->clearance_height)
{
map_obstruction_set_error_text(tileElement);
return MONEY32_UNDEFINED;
}
} while (!(tileElement++)->IsLastForTile());
}
for (int32_t i = 0; i < 4; i += 1)
{
int32_t cornerHeight = tile_element_get_corner_height(surfaceElement, i);
cornerHeight -= map_get_corner_height(height, style & TILE_ELEMENT_SURFACE_SLOPE_MASK, i);
cost += MONEY(abs(cornerHeight) * 5 / 2, 0);
}
if (flags & GAME_COMMAND_FLAG_APPLY)
{
if (gGameCommandNestLevel == 1)
{
LocationXYZ16 coord;
coord.x = x + 16;
coord.y = y + 16;
coord.z = tile_element_height(coord.x, coord.y);
network_set_player_last_action_coord(network_get_player_index(game_command_playerid), coord);
}
surfaceElement = map_get_surface_element_at({ x, y });
surfaceElement->base_height = height;
surfaceElement->clearance_height = height;
surfaceElement->AsSurface()->SetSlope(style);
int32_t waterHeight = surfaceElement->AsSurface()->GetWaterHeight();
if (waterHeight != 0 && waterHeight <= height / 2)
surfaceElement->AsSurface()->SetWaterHeight(0);
map_invalidate_tile_full(x, y);
}
if (gParkFlags & PARK_FLAGS_NO_MONEY)
return 0;
return cost;
}
void game_command_set_land_height(
int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, [[maybe_unused]] int32_t* esi, [[maybe_unused]] int32_t* edi,
[[maybe_unused]] int32_t* ebp)
{
*ebx = map_set_land_height(*ebx & 0xFF, *eax & 0xFFFF, *ecx & 0xFFFF, *edx & 0xFF, (*edx >> 8) & 0xFF);
}
static money32 map_set_land_ownership(uint8_t flags, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint8_t newOwnership)
{
gCommandExpenditureType = RCT_EXPENDITURE_TYPE_LAND_PURCHASE;
@ -1508,12 +1257,14 @@ static money32 raise_land(
height += 2;
slope &= TILE_ELEMENT_SURFACE_SLOPE_MASK;
money32 tileCost = map_set_land_height(flags, x_coord, y_coord, height, slope);
if (tileCost == MONEY32_UNDEFINED)
auto landSetHeightAction = LandSetHeightAction({ x_coord, y_coord }, height, slope);
auto res = (flags & GAME_COMMAND_FLAG_APPLY) ? landSetHeightAction.Execute() : landSetHeightAction.Query();
if (res->Error != GA_ERROR::OK)
{
return MONEY32_UNDEFINED;
}
cost += tileCost;
cost += res->Cost;
}
}
}
@ -1575,12 +1326,15 @@ static money32 lower_land(
height -= 2;
newSlope &= TILE_ELEMENT_SURFACE_SLOPE_MASK;
money32 tileCost = map_set_land_height(flags, x_coord, y_coord, height, newSlope);
if (tileCost == MONEY32_UNDEFINED)
auto landSetHeightAction = LandSetHeightAction({ x_coord, y_coord }, height, newSlope);
auto res = (flags & GAME_COMMAND_FLAG_APPLY) ? landSetHeightAction.Execute() : landSetHeightAction.Query();
if (res->Error != GA_ERROR::OK)
{
return MONEY32_UNDEFINED;
}
cost += tileCost;
cost += res->Cost;
}
}
}
@ -1824,7 +1578,18 @@ static money32 smooth_land_tile(
slope &= ~SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT;
}
}
return game_do_command(x, flags, y, targetBaseZ | (slope << 8), GAME_COMMAND_SET_LAND_HEIGHT, 0, 0);
auto landSetHeightAction = LandSetHeightAction({ x, y }, targetBaseZ, slope);
auto res = (flags & GAME_COMMAND_FLAG_APPLY) ? landSetHeightAction.Execute() : landSetHeightAction.Query();
if (res->Error == GA_ERROR::OK)
{
return res->Cost;
}
else
{
return MONEY32_UNDEFINED;
}
}
static money32 smooth_land_row_by_edge(
@ -1835,7 +1600,6 @@ static money32 smooth_land_row_by_edge(
int32_t landChangePerTile = raiseLand ? -2 : 2;
TileElement *tileElement, *nextTileElement;
money32 totalCost = 0;
money32 result;
// check if we need to start at all
if (!map_is_location_valid({ x, y }) || !map_is_location_valid({ x + stepX, y + stepY }))
@ -1960,10 +1724,12 @@ static money32 smooth_land_row_by_edge(
}
}
}
result = game_do_command(x, flags, y, targetBaseZ | (slope << 8), GAME_COMMAND_SET_LAND_HEIGHT, 0, 0);
if (result != MONEY32_UNDEFINED)
auto landSetHeightAction = LandSetHeightAction({ x, y }, targetBaseZ, slope);
auto res = (flags & GAME_COMMAND_FLAG_APPLY) ? landSetHeightAction.Execute() : landSetHeightAction.Query();
if (res->Error == GA_ERROR::OK)
{
totalCost += result;
totalCost += res->Cost;
}
}
return totalCost;

View File

@ -183,8 +183,6 @@ money32 wall_place(
int32_t type, int32_t x, int32_t y, int32_t z, int32_t edge, int32_t primaryColour, int32_t secondaryColour,
int32_t tertiaryColour, int32_t flags);
void game_command_set_land_height(
int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp);
void game_command_set_land_ownership(
int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp);
void game_command_remove_banner(
@ -258,6 +256,8 @@ void map_invalidate_region(const LocationXY16& mins, const LocationXY16& maxs);
int32_t map_get_tile_side(int32_t mapX, int32_t mapY);
int32_t map_get_tile_quadrant(int32_t mapX, int32_t mapY);
int32_t map_get_corner_height(int32_t z, int32_t slope, int32_t direction);
int32_t tile_element_get_corner_height(const TileElement* tileElement, int32_t direction);
void map_clear_all_elements();