diff --git a/src/openrct2-ui/interface/ViewportInteraction.cpp b/src/openrct2-ui/interface/ViewportInteraction.cpp index 5402b6fa34..32971b1163 100644 --- a/src/openrct2-ui/interface/ViewportInteraction.cpp +++ b/src/openrct2-ui/interface/ViewportInteraction.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -465,10 +466,11 @@ int32_t viewport_interaction_right_click(int32_t x, int32_t y) */ static void viewport_interaction_remove_scenery(TileElement* tileElement, int32_t x, int32_t y) { - gGameCommandErrorTitle = STR_CANT_REMOVE_THIS; - game_do_command( - x, (tileElement->AsSmallScenery()->GetSceneryQuadrant() << 8) | GAME_COMMAND_FLAG_APPLY, y, - (tileElement->AsSmallScenery()->GetEntryIndex() << 8) | tileElement->base_height, GAME_COMMAND_REMOVE_SCENERY, 0, 0); + auto removeSceneryAction = SceneryRemoveSmallAction( + x, y, tileElement->base_height, tileElement->AsSmallScenery()->GetSceneryQuadrant(), + tileElement->AsSmallScenery()->GetEntryIndex()); + + GameActions::Execute(&removeSceneryAction); } /** diff --git a/src/openrct2/Game.cpp b/src/openrct2/Game.cpp index 57429537c2..c3936aee95 100644 --- a/src/openrct2/Game.cpp +++ b/src/openrct2/Game.cpp @@ -1469,7 +1469,7 @@ GAME_COMMAND_POINTER* new_game_command_table[GAME_COMMAND_COUNT] = { game_command_set_ride_setting, game_command_place_ride_entrance_or_exit, game_command_remove_ride_entrance_or_exit, - game_command_remove_scenery, + nullptr, game_command_place_scenery, game_command_set_water_height, game_command_place_footpath, diff --git a/src/openrct2/actions/GameActionRegistration.cpp b/src/openrct2/actions/GameActionRegistration.cpp index d5c77b8671..531f627bfb 100644 --- a/src/openrct2/actions/GameActionRegistration.cpp +++ b/src/openrct2/actions/GameActionRegistration.cpp @@ -24,6 +24,7 @@ #include "RideSetColourScheme.hpp" #include "RideSetName.hpp" #include "RideSetStatus.hpp" +#include "SceneryRemoveSmallAction.hpp" #include "SetParkEntranceFeeAction.hpp" #include "SignSetNameAction.hpp" #include "StaffSetColourAction.hpp" @@ -55,5 +56,6 @@ namespace GameActions Register(); Register(); Register(); + Register(); } } // namespace GameActions diff --git a/src/openrct2/actions/SceneryRemoveSmallAction.hpp b/src/openrct2/actions/SceneryRemoveSmallAction.hpp new file mode 100644 index 0000000000..c281984032 --- /dev/null +++ b/src/openrct2/actions/SceneryRemoveSmallAction.hpp @@ -0,0 +1,168 @@ +/***************************************************************************** + * 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 "../Cheats.h" +#include "../OpenRCT2.h" +#include "../common.h" +#include "../core/MemoryStream.h" +#include "../interface/Window.h" +#include "../localisation/Localisation.h" +#include "../localisation/StringIds.h" +#include "../management/Finance.h" +#include "../ride/Ride.h" +#include "../world/Park.h" +#include "../world/SmallScenery.h" +#include "../world/Sprite.h" +#include "GameAction.h" + +DEFINE_GAME_ACTION(SceneryRemoveSmallAction, GAME_COMMAND_REMOVE_SCENERY, GameActionResult) +{ +private: + int16_t _x; + int16_t _y; + uint8_t _baseHeight; + uint8_t _quadrant; + uint8_t _sceneryType; + +public: + SceneryRemoveSmallAction() = default; + + SceneryRemoveSmallAction(int16_t x, int16_t y, uint8_t baseHeight, uint8_t quadrant, uint8_t sceneryType) + : _x(x) + , _y(y) + , _baseHeight(baseHeight) + , _quadrant(quadrant) + , _sceneryType(sceneryType) + { + } + + uint16_t GetActionFlags() const override + { + return GameAction::GetActionFlags(); + } + + void Serialise(DataSerialiser & stream) override + { + GameAction::Serialise(stream); + + stream << DS_TAG(_x) << DS_TAG(_y) << DS_TAG(_baseHeight) << DS_TAG(_quadrant) << DS_TAG(_sceneryType); + } + + GameActionResult::Ptr Query() const override + { + GameActionResult::Ptr res = std::make_unique(); + + if (!map_is_location_valid({ _x, _y })) + { + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_REMOVE_THIS, STR_LAND_NOT_OWNED_BY_PARK); + } + + rct_scenery_entry* entry = get_small_scenery_entry(_sceneryType); + if (entry == nullptr) + { + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_REMOVE_THIS, STR_INVALID_SELECTION_OF_OBJECTS); + } + + res->Cost = entry->small_scenery.removal_price * 10; + res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; + res->Position.x = _x; + res->Position.y = _y; + res->Position.z = _baseHeight * 8; + + if (!(gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && !(GetFlags() & GAME_COMMAND_FLAG_GHOST) && !gCheatsSandboxMode) + { + // Check if allowed to remove item + if (gParkFlags & PARK_FLAGS_FORBID_TREE_REMOVAL) + { + if (entry->small_scenery.height > 64) + { + res->Error = GA_ERROR::NO_CLEARANCE; + res->ErrorTitle = STR_CANT_REMOVE_THIS; + res->ErrorMessage = STR_FORBIDDEN_BY_THE_LOCAL_AUTHORITY; + return res; + } + } + + // Check if the land is owned + if (!map_is_location_owned(_x, _y, _baseHeight * 8)) + { + res->Error = GA_ERROR::NO_CLEARANCE; + res->ErrorTitle = STR_CANT_REMOVE_THIS; + res->ErrorMessage = STR_LAND_NOT_OWNED_BY_PARK; + return res; + } + } + + TileElement* tileElement = FindSceneryElement(); + if (tileElement == nullptr) + { + res->Cost = 0; + return res; + } + + return res; + } + + GameActionResult::Ptr Execute() const override + { + GameActionResult::Ptr res = std::make_unique(); + + rct_scenery_entry* entry = get_small_scenery_entry(_sceneryType); + if (entry == nullptr) + { + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_INVALID_SELECTION_OF_OBJECTS); + } + + res->Cost = entry->small_scenery.removal_price * 10; + res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; + res->Position.x = _x; + res->Position.y = _y; + res->Position.z = _baseHeight * 8; + + TileElement* tileElement = FindSceneryElement(); + if (tileElement == nullptr) + { + res->Cost = 0; + return res; + } + + res->Position.z = tile_element_height(res->Position.x, res->Position.y); + + map_invalidate_tile_full(_x, _y); + tile_element_remove(tileElement); + + return res; + } + +private: + TileElement* FindSceneryElement() const + { + TileElement* tileElement = map_get_first_element_at(_x / 32, _y / 32); + do + { + if (tileElement->GetType() != TILE_ELEMENT_TYPE_SMALL_SCENERY) + continue; + if ((tileElement->AsSmallScenery()->GetSceneryQuadrant()) != _quadrant) + continue; + if (tileElement->base_height != _baseHeight) + continue; + if (tileElement->AsSmallScenery()->GetEntryIndex() != _sceneryType) + continue; + if ((GetFlags() & GAME_COMMAND_FLAG_GHOST) && tileElement->IsGhost() == false) + continue; + + return tileElement; + + } while (!(tileElement++)->IsLastForTile()); + + return nullptr; + } +}; diff --git a/src/openrct2/ride/TrackDesign.cpp b/src/openrct2/ride/TrackDesign.cpp index e53d9f82f3..2dbdd765e8 100644 --- a/src/openrct2/ride/TrackDesign.cpp +++ b/src/openrct2/ride/TrackDesign.cpp @@ -12,6 +12,7 @@ #include "../Cheats.h" #include "../Game.h" #include "../OpenRCT2.h" +#include "../actions/SceneryRemoveSmallAction.hpp" #include "../actions/WallRemoveAction.hpp" #include "../audio/audio.h" #include "../core/File.h" @@ -828,9 +829,11 @@ static int32_t track_design_place_scenery( } z = (scenery->z * 8 + originZ) / 8; - game_do_command( - mapCoord.x, flags | quadrant << 8, mapCoord.y, (entry_index << 8) | z, GAME_COMMAND_REMOVE_SCENERY, - 0, 0); + + auto removeSceneryAction = SceneryRemoveSmallAction(mapCoord.x, mapCoord.y, z, quadrant, entry_index); + removeSceneryAction.SetFlags(flags); + removeSceneryAction.Execute(); + break; } case OBJECT_TYPE_LARGE_SCENERY: diff --git a/src/openrct2/world/Map.cpp b/src/openrct2/world/Map.cpp index 1059523993..bca95a8fa1 100644 --- a/src/openrct2/world/Map.cpp +++ b/src/openrct2/world/Map.cpp @@ -15,6 +15,7 @@ #include "../Input.h" #include "../OpenRCT2.h" #include "../actions/FootpathRemoveAction.hpp" +#include "../actions/SceneryRemoveSmallAction.hpp" #include "../actions/WallRemoveAction.hpp" #include "../audio/audio.h" #include "../config/Config.h" @@ -1078,17 +1079,17 @@ restart_from_beginning: case TILE_ELEMENT_TYPE_SMALL_SCENERY: if (clear & (1 << 0)) { - int32_t eax = x * 32; - int32_t ebx = (tileElement->AsSmallScenery()->GetSceneryQuadrant() << 8) | flags; - int32_t ecx = y * 32; - int32_t edx = (tileElement->AsSmallScenery()->GetEntryIndex() << 8) | (tileElement->base_height); - int32_t edi = 0, ebp = 0; - cost = game_do_command(eax, ebx, ecx, edx, GAME_COMMAND_REMOVE_SCENERY, edi, ebp); + auto removeSceneryAction = SceneryRemoveSmallAction( + x * 32, y * 32, tileElement->base_height, tileElement->AsSmallScenery()->GetSceneryQuadrant(), + tileElement->AsSmallScenery()->GetEntryIndex()); + removeSceneryAction.SetFlags(flags); - if (cost == MONEY32_UNDEFINED) + auto res + = ((flags & GAME_COMMAND_FLAG_APPLY) ? removeSceneryAction.Execute() : removeSceneryAction.Query()); + if (res->Error != GA_ERROR::OK) return MONEY32_UNDEFINED; - totalCost += cost; + totalCost += res->Cost; if (flags & GAME_COMMAND_FLAG_APPLY) goto restart_from_beginning; diff --git a/src/openrct2/world/Map.h b/src/openrct2/world/Map.h index 2c037d08d7..481a70ef0d 100644 --- a/src/openrct2/world/Map.h +++ b/src/openrct2/world/Map.h @@ -187,8 +187,6 @@ 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_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_remove_large_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_remove_banner( diff --git a/src/openrct2/world/Scenery.cpp b/src/openrct2/world/Scenery.cpp index e88f54b59e..0ab8f0237a 100644 --- a/src/openrct2/world/Scenery.cpp +++ b/src/openrct2/world/Scenery.cpp @@ -12,6 +12,7 @@ #include "../Cheats.h" #include "../Context.h" #include "../Game.h" +#include "../actions/SceneryRemoveSmallAction.hpp" #include "../actions/WallRemoveAction.hpp" #include "../common.h" #include "../localisation/Localisation.h" @@ -184,10 +185,10 @@ void scenery_remove_ghost_tool_placement() if (gSceneryGhostType & SCENERY_GHOST_FLAG_0) { gSceneryGhostType &= ~SCENERY_GHOST_FLAG_0; - uint8_t flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_5 - | GAME_COMMAND_FLAG_GHOST; - game_do_command( - x, flags | (gSceneryQuadrant << 8), y, z | (gSceneryPlaceObject << 8), GAME_COMMAND_REMOVE_SCENERY, 0, 0); + + auto removeSceneryAction = SceneryRemoveSmallAction(x, y, z, gSceneryQuadrant, gSceneryPlaceObject); + removeSceneryAction.SetFlags(GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_5 | GAME_COMMAND_FLAG_GHOST); + removeSceneryAction.Execute(); } if (gSceneryGhostType & SCENERY_GHOST_FLAG_1) diff --git a/src/openrct2/world/SmallScenery.cpp b/src/openrct2/world/SmallScenery.cpp index 97ae0958f4..2ccd2f4501 100644 --- a/src/openrct2/world/SmallScenery.cpp +++ b/src/openrct2/world/SmallScenery.cpp @@ -24,95 +24,6 @@ #include "Scenery.h" #include "Surface.h" -static money32 SmallSceneryRemove( - int16_t x, int16_t y, uint8_t baseHeight, uint8_t quadrant, uint8_t sceneryType, uint8_t flags) -{ - if (!map_is_location_valid({ x, y })) - { - return MONEY32_UNDEFINED; - } - money32 cost; - - rct_scenery_entry* entry = get_small_scenery_entry(sceneryType); - if (entry == nullptr) - { - log_warning("Invalid game command for scenery removal, scenery_type = %u", sceneryType); - return MONEY32_UNDEFINED; - } - cost = entry->small_scenery.removal_price * 10; - - gCommandExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; - gCommandPosition.x = x + 16; - gCommandPosition.y = y + 16; - gCommandPosition.z = baseHeight * 8; - - if (!(flags & GAME_COMMAND_FLAG_GHOST) && game_is_paused() && !gCheatsBuildInPauseMode) - { - gGameCommandErrorText = STR_CONSTRUCTION_NOT_POSSIBLE_WHILE_GAME_IS_PAUSED; - return MONEY32_UNDEFINED; - } - - if (!(gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && !(flags & GAME_COMMAND_FLAG_GHOST) && !gCheatsSandboxMode) - { - // Check if allowed to remove item - if (gParkFlags & PARK_FLAGS_FORBID_TREE_REMOVAL) - { - if (entry->small_scenery.height > 64) - { - gGameCommandErrorText = STR_FORBIDDEN_BY_THE_LOCAL_AUTHORITY; - return MONEY32_UNDEFINED; - } - } - - // Check if the land is owned - if (!map_is_location_owned(x, y, gCommandPosition.z)) - { - return MONEY32_UNDEFINED; - } - } - - bool sceneryFound = false; - TileElement* tileElement = map_get_first_element_at(x / 32, y / 32); - do - { - if (tileElement->GetType() != TILE_ELEMENT_TYPE_SMALL_SCENERY) - continue; - if ((tileElement->AsSmallScenery()->GetSceneryQuadrant()) != quadrant) - continue; - if (tileElement->base_height != baseHeight) - continue; - if (tileElement->AsSmallScenery()->GetEntryIndex() != sceneryType) - continue; - if ((flags & GAME_COMMAND_FLAG_GHOST) && !(tileElement->flags & TILE_ELEMENT_FLAG_GHOST)) - continue; - - sceneryFound = true; - break; - } while (!(tileElement++)->IsLastForTile()); - - if (!sceneryFound) - { - return 0; - } - - // Remove element - if (flags & GAME_COMMAND_FLAG_APPLY) - { - if (gGameCommandNestLevel == 1 && !(flags & GAME_COMMAND_FLAG_GHOST)) - { - 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); - } - - map_invalidate_tile_full(x, y); - tile_element_remove(tileElement); - } - return (gParkFlags & PARK_FLAGS_NO_MONEY) ? 0 : cost; -} - static money32 SmallScenerySetColour( int16_t x, int16_t y, uint8_t baseHeight, uint8_t quadrant, uint8_t sceneryType, uint8_t primaryColour, uint8_t secondaryColour, uint8_t flags) @@ -412,17 +323,6 @@ static money32 SmallSceneryPlace( return cost; } -/** - * - * rct2: 0x006E0E01 - */ -void game_command_remove_scenery( - 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 = SmallSceneryRemove(*eax & 0xFFFF, *ecx & 0xFFFF, *edx & 0xFF, ((*ebx >> 8) & 0xFF), (*edx >> 8) & 0xFF, *ebx & 0xFF); -} - /** * * rct2: 0x006E0F26