From ea585950f61892d68d63700e43e532c61d21a594 Mon Sep 17 00:00:00 2001 From: duncanspumpkin Date: Thu, 7 Feb 2019 10:37:53 +0000 Subject: [PATCH] Refactor. Split out into multiple functions. --- src/openrct2/actions/LandSetHeightAction.hpp | 396 +++++++++++-------- 1 file changed, 223 insertions(+), 173 deletions(-) diff --git a/src/openrct2/actions/LandSetHeightAction.hpp b/src/openrct2/actions/LandSetHeightAction.hpp index a1043a1b41..2bc0e40f77 100644 --- a/src/openrct2/actions/LandSetHeightAction.hpp +++ b/src/openrct2/actions/LandSetHeightAction.hpp @@ -10,9 +10,11 @@ #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" @@ -75,29 +77,10 @@ public: return std::make_unique(GA_ERROR::DISALLOWED, STR_FORBIDDEN_BY_THE_LOCAL_AUTHORITY); } - if (_coords.x > gMapSizeMaxXY || _coords.y > gMapSizeMaxXY) + rct_string_id errorTitle = CheckParameters(); + if (errorTitle != STR_NONE) { - return std::make_unique(GA_ERROR::INVALID_PARAMETERS, STR_OFF_EDGE_OF_MAP); - } - - if (_height < MINIMUM_LAND_HEIGHT) - { - return std::make_unique(GA_ERROR::INVALID_PARAMETERS, STR_TOO_LOW); - } - - // Divide by 2 and subtract 7 to get the in-game units. - if (_height > MAXIMUM_LAND_HEIGHT) - { - return std::make_unique(GA_ERROR::INVALID_PARAMETERS, STR_TOO_HIGH); - } - else if (_height > MAXIMUM_LAND_HEIGHT - 2 && (_style & 0x1F) != 0) - { - return std::make_unique(GA_ERROR::INVALID_PARAMETERS, STR_TOO_HIGH); - } - - if (_height == MAXIMUM_LAND_HEIGHT - 2 && (_style & 0x10)) - { - return std::make_unique(GA_ERROR::INVALID_PARAMETERS, STR_TOO_HIGH); + return std::make_unique(GA_ERROR::DISALLOWED, errorTitle); } if (!(gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && !gCheatsSandboxMode) @@ -108,146 +91,67 @@ public: } } - money32 cost = MONEY(0, 0); - + money32 cost{}; if (!gCheatsDisableClearanceChecks) { - // Check for obstructing scenery - TileElement* tileElement = map_get_first_element_at(_coords.x / 32, _coords.y / 32); - do + if (gParkFlags & PARK_FLAGS_FORBID_TREE_REMOVAL) { - 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) + // Check for obstructing large trees + TileElement* tileElement = CheckTallTreeObstructions(); + if (tileElement != nullptr) { map_obstruction_set_error_text(tileElement); return std::make_unique(GA_ERROR::DISALLOWED, gGameCommandErrorText); } - cost += MONEY(sceneryEntry->small_scenery.removal_price, 0); - } while (!(tileElement++)->IsLastForTile()); + } + cost = GetSmallSceneryRemovalCost(); } // Check for ride support limits if (!gCheatsDisableSupportLimits) { - TileElement* tileElement = map_get_first_element_at(_coords.x / 32, _coords.y / 32); - do + errorTitle = CheckRideSupports(); + if (errorTitle != STR_NONE) { - 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 std::make_unique(GA_ERROR::DISALLOWED, STR_SUPPORTS_CANT_BE_EXTENDED); - } - } - } - } - } while (!(tileElement++)->IsLastForTile()); + return std::make_unique(GA_ERROR::DISALLOWED, errorTitle); + } } - uint8_t zCorner = _height; // z position of highest corner of tile TileElement* surfaceElement = map_get_surface_element_at(_coords); - if (surfaceElement->AsSurface()->HasTrackThatNeedsWater()) + TileElement* tileElement = CheckFloatingStructures(surfaceElement, _height); + if (tileElement != nullptr) { - 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 std::make_unique(GA_ERROR::DISALLOWED, gGameCommandErrorText); - } - } - } - - zCorner = _height; - if (_style & 0xF) - { - zCorner += 2; - if (_style & 0x10) - { - zCorner += 2; - } + map_obstruction_set_error_text(tileElement); + return std::make_unique(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(GA_ERROR::DISALLOWED, STR_NONE); + return std::make_unique(GA_ERROR::DISALLOWED, gGameCommandErrorText); + } + + tileElement = CheckUnremovableObstructions(surfaceElement, zCorner); + if (tileElement != nullptr) + { + map_obstruction_set_error_text(tileElement); + return std::make_unique(GA_ERROR::DISALLOWED, gGameCommandErrorText); } } - - if (!gCheatsDisableClearanceChecks) - { - TileElement* tileElement = map_get_first_element_at(_coords.x / 32, _coords.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 std::make_unique(GA_ERROR::DISALLOWED, gGameCommandErrorText); - } - continue; - } - if (_height < tileElement->clearance_height) - { - map_obstruction_set_error_text(tileElement); - return std::make_unique(GA_ERROR::DISALLOWED, gGameCommandErrorText); - } - } 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); - } - auto res = std::make_unique(); - res->Cost = cost; + res->Cost = cost + GetSurfaceHeightChangeCost(surfaceElement); res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; return res; } @@ -255,81 +159,227 @@ public: GameActionResult::Ptr Execute() const override { money32 cost = MONEY(0, 0); - footpath_remove_litter(_coords.x, _coords.y, tile_element_height(_coords.x, _coords.y)); - if (!gCheatsDisableClearanceChecks) - wall_remove_at(_coords.x, _coords.y, _height * 8 - 16, _height * 8 + 32); + auto surfaceHeight = tile_element_height(_coords.x, _coords.y) & 0xFFFF; + footpath_remove_litter(_coords.x, _coords.y, surfaceHeight); if (!gCheatsDisableClearanceChecks) { - 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 && gParkFlags & PARK_FLAGS_FORBID_TREE_REMOVAL) - { - map_obstruction_set_error_text(tileElement); - return std::make_unique(GA_ERROR::DISALLOWED, gGameCommandErrorText); - } - cost += MONEY(sceneryEntry->small_scenery.removal_price, 0); - tile_element_remove(tileElement--); - } while (!(tileElement++)->IsLastForTile()); + wall_remove_at(_coords.x, _coords.y, _height * 8 - 16, _height * 8 + 32); + cost += GetSmallSceneryRemovalCost(); + SmallSceneryRemoval(); } - uint8_t zCorner = _height; // z position of highest corner of tile 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(); + 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 & 0x1F) + if (_style & TILE_ELEMENT_SURFACE_SLOPE_MASK) { zCorner += 2; - if (_style & 0x10) + if (_style & TILE_ELEMENT_SURFACE_DIAGONAL_FLAG) { zCorner += 2; } } if (zCorner > waterHeight * 2 - 2) { - surfaceElement++; - map_obstruction_set_error_text(surfaceElement); - return std::make_unique(GA_ERROR::DISALLOWED, gGameCommandErrorText); + 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; + } - LocationXYZ16 coord; - coord.x = _coords.x + 16; - coord.y = _coords.y + 16; - coord.z = tile_element_height(_coords.x, _coords.y); - network_set_player_last_action_coord(network_get_player_index(game_command_playerid), coord); - - surfaceElement = map_get_surface_element_at(_coords); + 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); + } - auto res = std::make_unique(); - res->Cost = cost; - res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; - return res; + map_invalidate_tile_full(_coords.x, _coords.y); } };