diff --git a/src/openrct2-ui/title/TitleSequencePlayer.cpp b/src/openrct2-ui/title/TitleSequencePlayer.cpp index 30aef26573..05872d80d8 100644 --- a/src/openrct2-ui/title/TitleSequencePlayer.cpp +++ b/src/openrct2-ui/title/TitleSequencePlayer.cpp @@ -57,7 +57,7 @@ private: int32_t _lastScreenWidth = 0; int32_t _lastScreenHeight = 0; - CoordsXY _viewCentreLocation = {}; + CoordsXY _viewCentreLocation; public: explicit TitleSequencePlayer(IScenarioRepository& scenarioRepository, GameState& gameState) diff --git a/src/openrct2-ui/windows/TopToolbar.cpp b/src/openrct2-ui/windows/TopToolbar.cpp index f11f255355..48a64c1115 100644 --- a/src/openrct2-ui/windows/TopToolbar.cpp +++ b/src/openrct2-ui/windows/TopToolbar.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -292,6 +293,8 @@ static void toggle_water_window(rct_window* topToolbar, rct_widgetindex widgetIn static money32 selection_lower_land(uint8_t flags); static money32 selection_raise_land(uint8_t flags); +static ClearAction GetClearAction(); + static bool _menuDropdownIncludesTwitch; static uint8_t _unkF64F0E; static int16_t _unkF64F0A; @@ -1940,18 +1943,13 @@ static void top_toolbar_tool_update_scenery_clear(int16_t x, int16_t y) if (!state_changed) return; - int32_t eax = gMapSelectPositionA.x; - int32_t ecx = gMapSelectPositionA.y; - int32_t edi = gMapSelectPositionB.x; - int32_t ebp = gMapSelectPositionB.y; - int32_t clear = (gClearSmallScenery << 0) | (gClearLargeScenery << 1) | (gClearFootpath << 2); - money32 cost = game_do_command(eax, 0, ecx, clear, GAME_COMMAND_CLEAR_SCENERY, edi, ebp); - + auto action = GetClearAction(); + auto result = GameActions::Query(&action); + auto cost = (result->Error == GA_ERROR::OK ? result->Cost : MONEY32_UNDEFINED); if (gClearSceneryCost != cost) { gClearSceneryCost = cost; window_invalidate_by_class(WC_CLEAR_SCENERY); - return; } } @@ -2815,16 +2813,12 @@ static void window_top_toolbar_tool_down(rct_window* w, rct_widgetindex widgetIn switch (widgetIndex) { case WIDX_CLEAR_SCENERY: - if (!(gMapSelectFlags & MAP_SELECT_FLAG_ENABLE)) - break; - - gGameCommandErrorTitle = STR_UNABLE_TO_REMOVE_ALL_SCENERY_FROM_HERE; - - game_do_command( - gMapSelectPositionA.x, 1, gMapSelectPositionA.y, - ((gClearSmallScenery ? 1 : 0) | (gClearLargeScenery ? 1 : 0) << 1 | (gClearFootpath ? 1 : 0) << 2), - GAME_COMMAND_CLEAR_SCENERY, gMapSelectPositionB.x, gMapSelectPositionB.y); - gCurrentToolId = TOOL_CROSSHAIR; + if (gMapSelectFlags & MAP_SELECT_FLAG_ENABLE) + { + auto action = GetClearAction(); + GameActions::Execute(&action); + gCurrentToolId = TOOL_CROSSHAIR; + } break; case WIDX_LAND: if (gMapSelectFlags & MAP_SELECT_FLAG_ENABLE) @@ -3008,19 +3002,12 @@ static void window_top_toolbar_tool_drag(rct_window* w, rct_widgetindex widgetIn switch (widgetIndex) { case WIDX_CLEAR_SCENERY: - if (window_find_by_class(WC_ERROR) != nullptr) - break; - - if (!(gMapSelectFlags & MAP_SELECT_FLAG_ENABLE)) - break; - - gGameCommandErrorTitle = STR_UNABLE_TO_REMOVE_ALL_SCENERY_FROM_HERE; - - game_do_command( - gMapSelectPositionA.x, 1, gMapSelectPositionA.y, - ((gClearSmallScenery ? 1 : 0) | (gClearLargeScenery ? 1 : 0) << 1 | (gClearFootpath ? 1 : 0) << 2), - GAME_COMMAND_CLEAR_SCENERY, gMapSelectPositionB.x, gMapSelectPositionB.y); - gCurrentToolId = TOOL_CROSSHAIR; + if (window_find_by_class(WC_ERROR) == nullptr && (gMapSelectFlags & MAP_SELECT_FLAG_ENABLE)) + { + auto action = GetClearAction(); + GameActions::Execute(&action); + gCurrentToolId = TOOL_CROSSHAIR; + } break; case WIDX_LAND: // Custom setting to only change land style instead of raising or lowering land @@ -3539,3 +3526,19 @@ bool water_tool_is_active() return false; return true; } + +static ClearAction GetClearAction() +{ + auto range = MapRange(gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y); + + ClearableItems itemsToClear = 0; + + if (gClearSmallScenery) + itemsToClear |= CLEARABLE_ITEMS::SCENERY_SMALL; + if (gClearLargeScenery) + itemsToClear |= CLEARABLE_ITEMS::SCENERY_LARGE; + if (gClearFootpath) + itemsToClear |= CLEARABLE_ITEMS::SCENERY_FOOTPATH; + + return ClearAction(range, itemsToClear); +} diff --git a/src/openrct2/Game.cpp b/src/openrct2/Game.cpp index d0c32640c5..97574ac59c 100644 --- a/src/openrct2/Game.cpp +++ b/src/openrct2/Game.cpp @@ -1513,7 +1513,7 @@ GAME_COMMAND_POINTER* new_game_command_table[GAME_COMMAND_COUNT] = { game_command_set_large_scenery_colour, game_command_set_banner_colour, game_command_set_land_ownership, - game_command_clear_scenery, + nullptr, nullptr, nullptr, game_command_set_banner_style, diff --git a/src/openrct2/Game.h b/src/openrct2/Game.h index 000790d790..a5026ee75c 100644 --- a/src/openrct2/Game.h +++ b/src/openrct2/Game.h @@ -32,7 +32,7 @@ enum GAME_COMMAND GAME_COMMAND_SET_RIDE_SETTING, GAME_COMMAND_PLACE_RIDE_ENTRANCE_OR_EXIT, GAME_COMMAND_REMOVE_RIDE_ENTRANCE_OR_EXIT, - GAME_COMMAND_REMOVE_SCENERY, + GAME_COMMAND_REMOVE_SCENERY, // GA GAME_COMMAND_PLACE_SCENERY, GAME_COMMAND_SET_WATER_HEIGHT, GAME_COMMAND_PLACE_PATH, @@ -63,7 +63,7 @@ enum GAME_COMMAND GAME_COMMAND_PLACE_WALL, GAME_COMMAND_REMOVE_WALL, // GA GAME_COMMAND_PLACE_LARGE_SCENERY, - GAME_COMMAND_REMOVE_LARGE_SCENERY, + GAME_COMMAND_REMOVE_LARGE_SCENERY, // GA GAME_COMMAND_SET_CURRENT_LOAN, // GA GAME_COMMAND_SET_RESEARCH_FUNDING, // GA GAME_COMMAND_PLACE_TRACK_DESIGN, @@ -76,7 +76,7 @@ enum GAME_COMMAND GAME_COMMAND_SET_LARGE_SCENERY_COLOUR, GAME_COMMAND_SET_BANNER_COLOUR, GAME_COMMAND_SET_LAND_OWNERSHIP, - GAME_COMMAND_CLEAR_SCENERY, + GAME_COMMAND_CLEAR_SCENERY, // GA GAME_COMMAND_SET_BANNER_NAME, // GA GAME_COMMAND_SET_SIGN_NAME, // GA GAME_COMMAND_SET_BANNER_STYLE, diff --git a/src/openrct2/actions/ClearAction.hpp b/src/openrct2/actions/ClearAction.hpp new file mode 100644 index 0000000000..8406832fd6 --- /dev/null +++ b/src/openrct2/actions/ClearAction.hpp @@ -0,0 +1,247 @@ +/***************************************************************************** + * Copyright (c) 2014-2019 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 "../core/MemoryStream.h" +#include "../drawing/Drawing.h" +#include "../localisation/StringIds.h" +#include "../management/Finance.h" +#include "../world/LargeScenery.h" +#include "../world/Location.hpp" +#include "../world/Map.h" +#include "FootpathRemoveAction.hpp" +#include "GameAction.h" +#include "SceneryRemoveLargeAction.hpp" +#include "SceneryRemoveSmallAction.hpp" +#include "WallRemoveAction.hpp" + +#include + +using namespace OpenRCT2; + +using ClearableItems = uint8_t; + +namespace CLEARABLE_ITEMS +{ + constexpr ClearableItems SCENERY_SMALL = 1 << 0; + constexpr ClearableItems SCENERY_LARGE = 1 << 1; + constexpr ClearableItems SCENERY_FOOTPATH = 1 << 2; +} // namespace CLEARABLE_ITEMS + +DEFINE_GAME_ACTION(ClearAction, GAME_COMMAND_CLEAR_SCENERY, GameActionResult) +{ +private: + MapRange _range; + ClearableItems _itemsToClear; + +public: + ClearAction() + { + } + ClearAction(MapRange range, ClearableItems itemsToClear) + : _range(range) + , _itemsToClear(itemsToClear) + { + } + + void Serialise(DataSerialiser & stream) override + { + GameAction::Serialise(stream); + + stream << DS_TAG(_range) << DS_TAG(_itemsToClear); + } + + GameActionResult::Ptr Query() const override + { + return QueryExecute(false); + } + + GameActionResult::Ptr Execute() const override + { + return QueryExecute(true); + } + +private: + GameActionResult::Ptr CreateResult() const + { + auto result = MakeResult(); + result->ErrorTitle = STR_UNABLE_TO_REMOVE_ALL_SCENERY_FROM_HERE; + result->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; + + auto x = (_range.GetLeft() + _range.GetRight()) / 2 + 16; + auto y = (_range.GetTop() + _range.GetBottom()) / 2 + 16; + auto z = tile_element_height(x, y); + result->Position = CoordsXYZ(x, y, z); + + return result; + } + + GameActionResult::Ptr QueryExecute(bool executing) const + { + auto result = CreateResult(); + + auto noValidTiles = true; + auto error = GA_ERROR::OK; + rct_string_id errorMessage = STR_NONE; + money32 totalCost = 0; + + auto x0 = std::max(_range.GetLeft(), 32); + auto y0 = std::max(_range.GetTop(), 32); + auto x1 = std::min(_range.GetRight(), (int32_t)gMapSizeMaxXY); + auto y1 = std::min(_range.GetBottom(), (int32_t)gMapSizeMaxXY); + + for (int32_t y = y0; y <= y1; y += 32) + { + for (int32_t x = x0; x <= x1; x += 32) + { + if (MapCanClearAt(x, y)) + { + auto cost = ClearSceneryFromTile(x / 32, y / 32, executing); + if (cost != MONEY32_UNDEFINED) + { + noValidTiles = false; + totalCost += cost; + } + } + else + { + error = GA_ERROR::NOT_OWNED; + errorMessage = STR_LAND_NOT_OWNED_BY_PARK; + } + } + } + + if (_itemsToClear & CLEARABLE_ITEMS::SCENERY_LARGE) + { + ResetClearLargeSceneryFlag(); + } + + if (noValidTiles) + { + result->Error = error; + result->ErrorMessage = errorMessage; + } + + result->Cost = totalCost; + return result; + } + + money32 ClearSceneryFromTile(int32_t x, int32_t y, bool executing) const + { + // Pass down all flags. + TileElement* tileElement = nullptr; + money32 totalCost = 0; + bool tileEdited; + do + { + tileEdited = false; + tileElement = map_get_first_element_at(x, y); + do + { + auto type = tileElement->GetType(); + switch (type) + { + case TILE_ELEMENT_TYPE_PATH: + if (_itemsToClear & CLEARABLE_ITEMS::SCENERY_FOOTPATH) + { + auto footpathRemoveAction = FootpathRemoveAction(x * 32, y * 32, tileElement->base_height); + footpathRemoveAction.SetFlags(GetFlags()); + + auto res = executing ? footpathRemoveAction.Execute() : footpathRemoveAction.Query(); + if (res->Error != GA_ERROR::OK) + return MONEY32_UNDEFINED; + + totalCost += res->Cost; + tileEdited = executing; + } + break; + case TILE_ELEMENT_TYPE_SMALL_SCENERY: + if (_itemsToClear & CLEARABLE_ITEMS::SCENERY_SMALL) + { + auto removeSceneryAction = SceneryRemoveSmallAction( + x * 32, y * 32, tileElement->base_height, tileElement->AsSmallScenery()->GetSceneryQuadrant(), + tileElement->AsSmallScenery()->GetEntryIndex()); + removeSceneryAction.SetFlags(GetFlags()); + + auto res = executing ? removeSceneryAction.Execute() : removeSceneryAction.Query(); + if (res->Error != GA_ERROR::OK) + return MONEY32_UNDEFINED; + + totalCost += res->Cost; + tileEdited = executing; + } + break; + case TILE_ELEMENT_TYPE_WALL: + if (_itemsToClear & CLEARABLE_ITEMS::SCENERY_SMALL) + { + TileCoordsXYZD wallLocation = { x, y, tileElement->base_height, tileElement->GetDirection() }; + auto wallRemoveAction = WallRemoveAction(wallLocation); + wallRemoveAction.SetFlags(GetFlags()); + + auto res = executing ? wallRemoveAction.Execute() : wallRemoveAction.Query(); + if (res->Error != GA_ERROR::OK) + return MONEY32_UNDEFINED; + + totalCost += res->Cost; + tileEdited = executing; + } + break; + case TILE_ELEMENT_TYPE_LARGE_SCENERY: + if (_itemsToClear & CLEARABLE_ITEMS::SCENERY_LARGE) + { + auto removeSceneryAction = SceneryRemoveLargeAction( + x * 32, y * 32, tileElement->base_height, tileElement->GetDirection(), + tileElement->AsLargeScenery()->GetSequenceIndex()); + removeSceneryAction.SetFlags(GetFlags() | GAME_COMMAND_FLAG_PATH_SCENERY); + + auto res = executing ? removeSceneryAction.Execute() : removeSceneryAction.Query(); + if (res->Error != GA_ERROR::OK) + return MONEY32_UNDEFINED; + + totalCost += res->Cost; + tileEdited = executing; + } + break; + } + } while (!tileEdited && !(tileElement++)->IsLastForTile()); + } while (tileEdited); + + return totalCost; + } + + /** + * Function to clear the flag that is set to prevent cost duplication + * when using the clear scenery tool with large scenery. + */ + static void ResetClearLargeSceneryFlag() + { + // TODO: Improve efficiency of this + for (int32_t y = 0; y < MAXIMUM_MAP_SIZE_TECHNICAL; y++) + { + for (int32_t x = 0; x < MAXIMUM_MAP_SIZE_TECHNICAL; x++) + { + auto tileElement = map_get_first_element_at(x, y); + do + { + if (tileElement->GetType() == TILE_ELEMENT_TYPE_LARGE_SCENERY) + { + tileElement->flags &= ~(1 << 6); + } + } while (!(tileElement++)->IsLastForTile()); + } + } + } + + static bool MapCanClearAt(int32_t x, int32_t y) + { + return (gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) || gCheatsSandboxMode || map_is_location_owned_or_has_rights(x, y); + } +}; diff --git a/src/openrct2/actions/GameActionRegistration.cpp b/src/openrct2/actions/GameActionRegistration.cpp index f4ce2666d7..a350c15982 100644 --- a/src/openrct2/actions/GameActionRegistration.cpp +++ b/src/openrct2/actions/GameActionRegistration.cpp @@ -8,6 +8,7 @@ *****************************************************************************/ #include "BannerSetNameAction.hpp" +#include "ClearAction.hpp" #include "ClimateSetAction.hpp" #include "FootpathRemoveAction.hpp" #include "GameAction.h" @@ -59,5 +60,6 @@ namespace GameActions Register(); Register(); Register(); + Register(); } } // namespace GameActions diff --git a/src/openrct2/actions/SceneryRemoveSmallAction.hpp b/src/openrct2/actions/SceneryRemoveSmallAction.hpp index c281984032..49e4d8e043 100644 --- a/src/openrct2/actions/SceneryRemoveSmallAction.hpp +++ b/src/openrct2/actions/SceneryRemoveSmallAction.hpp @@ -146,6 +146,9 @@ private: TileElement* FindSceneryElement() const { TileElement* tileElement = map_get_first_element_at(_x / 32, _y / 32); + if (!tileElement) + return nullptr; + do { if (tileElement->GetType() != TILE_ELEMENT_TYPE_SMALL_SCENERY) diff --git a/src/openrct2/actions/WallRemoveAction.hpp b/src/openrct2/actions/WallRemoveAction.hpp index 4746113587..ea385c75dd 100644 --- a/src/openrct2/actions/WallRemoveAction.hpp +++ b/src/openrct2/actions/WallRemoveAction.hpp @@ -99,6 +99,9 @@ private: TileElement* GetFirstWallElementAt(const TileCoordsXYZD& location, bool isGhost) const { TileElement* tileElement = map_get_first_element_at(location.x, location.y); + if (!tileElement) + return nullptr; + do { if (tileElement->GetType() != TILE_ELEMENT_TYPE_WALL) diff --git a/src/openrct2/core/DataSerialiserTraits.h b/src/openrct2/core/DataSerialiserTraits.h index a95cebe730..c496398259 100644 --- a/src/openrct2/core/DataSerialiserTraits.h +++ b/src/openrct2/core/DataSerialiserTraits.h @@ -14,6 +14,7 @@ #include "../network/NetworkTypes.h" #include "../network/network.h" #include "../ride/Ride.h" +#include "../world/Location.hpp" #include "DataSerialiserTag.h" #include "Endianness.h" #include "MemoryStream.h" @@ -292,3 +293,31 @@ template struct DataSerializerTraitsWrite("}", 1); } }; + +template<> struct DataSerializerTraits +{ + static void encode(IStream* stream, const MapRange& v) + { + stream->WriteValue(ByteSwapBE(v.LeftTop.x)); + stream->WriteValue(ByteSwapBE(v.LeftTop.y)); + stream->WriteValue(ByteSwapBE(v.RightBottom.x)); + stream->WriteValue(ByteSwapBE(v.RightBottom.y)); + } + static void decode(IStream* stream, MapRange& v) + { + auto l = ByteSwapBE(stream->ReadValue()); + auto t = ByteSwapBE(stream->ReadValue()); + auto r = ByteSwapBE(stream->ReadValue()); + auto b = ByteSwapBE(stream->ReadValue()); + v = MapRange(l, t, r, b); + } + static void log(IStream* stream, const MapRange& v) + { + char coords[128] = {}; + snprintf( + coords, sizeof(coords), "MapRange(l = %d, t = %d, r = %d, b = %d)", v.LeftTop.x, v.LeftTop.y, v.RightBottom.x, + v.RightBottom.y); + + stream->Write(coords, strlen(coords)); + } +}; diff --git a/src/openrct2/paint/VirtualFloor.cpp b/src/openrct2/paint/VirtualFloor.cpp index a4b2c84195..43b4cb3b95 100644 --- a/src/openrct2/paint/VirtualFloor.cpp +++ b/src/openrct2/paint/VirtualFloor.cpp @@ -290,10 +290,10 @@ static void virtual_floor_get_tile_properties( void virtual_floor_paint(paint_session* session) { static constexpr const CoordsXY scenery_half_tile_offsets[4] = { - { -32, 0 }, - { 0, 32 }, - { 32, 0 }, - { 0, -32 }, + CoordsXY{ -32, 0 }, + CoordsXY{ 0, 32 }, + CoordsXY{ 32, 0 }, + CoordsXY{ 0, -32 }, }; if (_virtualFloorHeight < MINIMUM_LAND_HEIGHT) diff --git a/src/openrct2/world/Location.hpp b/src/openrct2/world/Location.hpp index 950a2937c6..66cf5e71d9 100644 --- a/src/openrct2/world/Location.hpp +++ b/src/openrct2/world/Location.hpp @@ -11,6 +11,8 @@ #include "../common.h" +#include + #define LOCATION_NULL ((int16_t)(uint16_t)0x8000) #define RCT_XY8_UNDEFINED 0xFFFF #define MakeXY16(x, y) \ @@ -59,7 +61,15 @@ constexpr int32_t COORDS_NULL = -1; */ struct CoordsXY { - int32_t x, y; + int32_t x = 0; + int32_t y = 0; + + CoordsXY() = default; + constexpr CoordsXY(int32_t _x, int32_t _y) + : x(_x) + , y(_y) + { + } }; struct TileCoordsXY @@ -86,7 +96,17 @@ struct TileCoordsXY struct CoordsXYZ { - int32_t x, y, z; + int32_t x = 0; + int32_t y = 0; + int32_t z = 0; + + CoordsXYZ() = default; + constexpr CoordsXYZ(int32_t _x, int32_t _y, int32_t _z) + : x(_x) + , y(_y) + , z(_z) + { + } }; struct TileCoordsXYZ @@ -150,3 +170,47 @@ struct TileCoordsXYZD return x == COORDS_NULL; }; }; + +/** + * Represents a rectangular range of the map using regular coordinates (32 per tile). + */ +struct MapRange +{ + CoordsXY LeftTop; + CoordsXY RightBottom; + + int32_t GetLeft() const + { + return LeftTop.x; + } + int32_t GetTop() const + { + return LeftTop.y; + } + int32_t GetRight() const + { + return RightBottom.x; + } + int32_t GetBottom() const + { + return RightBottom.y; + } + + MapRange() + : MapRange(0, 0, 0, 0) + { + } + MapRange(int32_t left, int32_t top, int32_t right, int32_t bottom) + : LeftTop(left, top) + , RightBottom(right, bottom) + { + } + + MapRange Normalise() const + { + auto result = MapRange( + std::min(GetLeft(), GetRight()), std::min(GetTop(), GetBottom()), std::max(GetLeft(), GetRight()), + std::max(GetTop(), GetBottom())); + return result; + } +}; diff --git a/src/openrct2/world/Map.cpp b/src/openrct2/world/Map.cpp index 41a2cf90ea..37bdc90f4b 100644 --- a/src/openrct2/world/Map.cpp +++ b/src/openrct2/world/Map.cpp @@ -873,203 +873,6 @@ void game_command_set_large_scenery_colour( *ebx = 0; } -// This will cause clear scenery to remove paths -// This should be a flag for the game command which can be set via a checkbox on the clear scenery window. -// #define CLEAR_SCENERY_REMOVES_PATHS - -/** - * - * rct2: 0x0068DFE4 - */ -static money32 map_clear_scenery_from_tile(int32_t x, int32_t y, int32_t clear, int32_t flags) -{ - int32_t type; - money32 totalCost; - TileElement* tileElement; - - totalCost = 0; - -restart_from_beginning: - tileElement = map_get_first_element_at(x, y); - do - { - type = tileElement->GetType(); - switch (type) - { - case TILE_ELEMENT_TYPE_PATH: - if (clear & (1 << 2)) - { - auto footpathRemoveAction = FootpathRemoveAction(x * 32, y * 32, tileElement->base_height); - footpathRemoveAction.SetFlags(flags); - auto res - = ((flags & GAME_COMMAND_FLAG_APPLY) ? footpathRemoveAction.Execute() : footpathRemoveAction.Query()); - if (res->Error == GA_ERROR::OK) - { - totalCost += res->Cost; - } - else - { - return MONEY32_UNDEFINED; - } - - if (flags & GAME_COMMAND_FLAG_APPLY) - goto restart_from_beginning; - } - break; - case TILE_ELEMENT_TYPE_SMALL_SCENERY: - if (clear & (1 << 0)) - { - auto removeSceneryAction = SceneryRemoveSmallAction( - x * 32, y * 32, tileElement->base_height, tileElement->AsSmallScenery()->GetSceneryQuadrant(), - tileElement->AsSmallScenery()->GetEntryIndex()); - removeSceneryAction.SetFlags(flags); - - auto res - = ((flags & GAME_COMMAND_FLAG_APPLY) ? removeSceneryAction.Execute() : removeSceneryAction.Query()); - if (res->Error != GA_ERROR::OK) - return MONEY32_UNDEFINED; - - totalCost += res->Cost; - - if (flags & GAME_COMMAND_FLAG_APPLY) - goto restart_from_beginning; - } - break; - case TILE_ELEMENT_TYPE_WALL: - if (clear & (1 << 0)) - { - // NOTE: We execute the game action directly as this function is already called from such. - TileCoordsXYZD wallLocation = { x, y, tileElement->base_height, tileElement->GetDirection() }; - auto wallRemoveAction = WallRemoveAction(wallLocation); - wallRemoveAction.SetFlags(flags); - auto res = ((flags & GAME_COMMAND_FLAG_APPLY) ? wallRemoveAction.Execute() : wallRemoveAction.Query()); - if (res->Error == GA_ERROR::OK) - { - totalCost += res->Cost; - } - } - break; - case TILE_ELEMENT_TYPE_LARGE_SCENERY: - if (clear & (1 << 1)) - { - auto removeSceneryAction = SceneryRemoveLargeAction( - x * 32, y * 32, tileElement->base_height, tileElement->GetDirection(), - tileElement->AsLargeScenery()->GetSequenceIndex()); - removeSceneryAction.SetFlags(flags | GAME_COMMAND_FLAG_PATH_SCENERY); - - auto res - = ((flags & GAME_COMMAND_FLAG_APPLY) ? removeSceneryAction.Execute() : removeSceneryAction.Query()); - if (res->Error != GA_ERROR::OK) - return MONEY32_UNDEFINED; - - totalCost += res->Cost; - - if (flags & GAME_COMMAND_FLAG_APPLY) - goto restart_from_beginning; - } - break; - } - } while (!(tileElement++)->IsLastForTile()); - - return totalCost; -} - -/** - * Function to clear the flag that is set to prevent cost duplication - * when using the clear scenery tool with large scenery. - */ -static void map_reset_clear_large_scenery_flag() -{ - TileElement* tileElement; - // TODO: Improve efficiency of this - for (int32_t y = 0; y < MAXIMUM_MAP_SIZE_TECHNICAL; y++) - { - for (int32_t x = 0; x < MAXIMUM_MAP_SIZE_TECHNICAL; x++) - { - tileElement = map_get_first_element_at(x, y); - do - { - if (tileElement->GetType() == TILE_ELEMENT_TYPE_LARGE_SCENERY) - { - tileElement->flags &= ~(1 << 6); - } - } while (!(tileElement++)->IsLastForTile()); - } - } -} - -money32 map_clear_scenery(int32_t x0, int32_t y0, int32_t x1, int32_t y1, int32_t clear, int32_t flags) -{ - int32_t x, y, z; - money32 totalCost, cost; - bool noValidTiles; - - gCommandExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; - - x = (x0 + x1) / 2 + 16; - y = (y0 + y1) / 2 + 16; - z = tile_element_height(x, y); - gCommandPosition.x = x; - gCommandPosition.y = y; - gCommandPosition.z = z; - - x0 = std::max(x0, 32); - y0 = std::max(y0, 32); - x1 = std::min(x1, (int32_t)gMapSizeMaxXY); - y1 = std::min(y1, (int32_t)gMapSizeMaxXY); - - noValidTiles = true; - totalCost = 0; - for (y = y0; y <= y1; y += 32) - { - for (x = x0; x <= x1; x += 32) - { - if ((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) || gCheatsSandboxMode - || map_is_location_owned_or_has_rights(x, y)) - { - cost = map_clear_scenery_from_tile(x / 32, y / 32, clear, flags); - if (cost != MONEY32_UNDEFINED) - { - noValidTiles = false; - totalCost += cost; - } - } - else - { - gGameCommandErrorText = STR_LAND_NOT_OWNED_BY_PARK; - } - } - } - - if (gGameCommandNestLevel == 1 && flags & GAME_COMMAND_FLAG_APPLY) - { - LocationXYZ16 coord; - coord.x = ((x0 + x1) / 2) + 16; - coord.y = ((y0 + y1) / 2) + 16; - coord.z = tile_element_height(coord.x, coord.y); - network_set_player_last_action_coord(network_get_player_index(game_command_playerid), coord); - } - - if (clear & (1 << 1)) - { - map_reset_clear_large_scenery_flag(); - } - - return noValidTiles ? MONEY32_UNDEFINED : totalCost; -} - -/** - * - * rct2: 0x0068DF91 - */ -void game_command_clear_scenery( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, [[maybe_unused]] int32_t* esi, int32_t* edi, int32_t* ebp) -{ - *ebx = map_clear_scenery( - (int16_t)(*eax & 0xFFFF), (int16_t)(*ecx & 0xFFFF), (int16_t)(*edi & 0xFFFF), (int16_t)(*ebp & 0xFFFF), *edx, - *ebx & 0xFF); -} - /** * * rct2: 0x00663CCD diff --git a/src/openrct2/world/Map.h b/src/openrct2/world/Map.h index cee7ab1607..e0fa34a4ab 100644 --- a/src/openrct2/world/Map.h +++ b/src/openrct2/world/Map.h @@ -197,8 +197,6 @@ void game_command_set_large_scenery_colour( 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_banner_colour( int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp); -void game_command_clear_scenery( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp); void game_command_change_surface_style( int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp); void game_command_raise_land(int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp);