diff --git a/OpenRCT2.xcodeproj/project.pbxproj b/OpenRCT2.xcodeproj/project.pbxproj index 6e09ee9292..4ca488c0ba 100644 --- a/OpenRCT2.xcodeproj/project.pbxproj +++ b/OpenRCT2.xcodeproj/project.pbxproj @@ -33,6 +33,7 @@ 2A5354E922099C4F00A5440F /* Network.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2A5354E822099C4F00A5440F /* Network.cpp */; }; 2A5C1368221E9F9000F8C245 /* TrackRemoveAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2A5C1367221E9F9000F8C245 /* TrackRemoveAction.hpp */; }; 2A61CAFB2229E5C50095AD67 /* RideEntranceExitPlaceAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2A61CAFA2229E5C50095AD67 /* RideEntranceExitPlaceAction.hpp */; }; + 2A61CAF92229E59F0095AD67 /* WaterSetHeightAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2A61CAF82229E59F0095AD67 /* WaterSetHeightAction.hpp */; }; 2AA050322209A8E300D3A922 /* StaffSetCostumeAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2AA050302209A8E300D3A922 /* StaffSetCostumeAction.hpp */; }; 2AA050332209A8E300D3A922 /* StaffSetOrdersAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2AA050312209A8E300D3A922 /* StaffSetOrdersAction.hpp */; }; 2AAFD7FA220DD2DC002461A4 /* TrackPlaceAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2AAFD7F9220DD2DC002461A4 /* TrackPlaceAction.hpp */; }; @@ -640,6 +641,7 @@ 2A5354EB22099D7700A5440F /* SignSetStyleAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SignSetStyleAction.hpp; sourceTree = ""; }; 2A5C1367221E9F9000F8C245 /* TrackRemoveAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TrackRemoveAction.hpp; sourceTree = ""; }; 2A61CAFA2229E5C50095AD67 /* RideEntranceExitPlaceAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = RideEntranceExitPlaceAction.hpp; sourceTree = ""; }; + 2A61CAF82229E59F0095AD67 /* WaterSetHeightAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = WaterSetHeightAction.hpp; sourceTree = ""; }; 2AA050302209A8E300D3A922 /* StaffSetCostumeAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = StaffSetCostumeAction.hpp; sourceTree = ""; }; 2AA050312209A8E300D3A922 /* StaffSetOrdersAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = StaffSetOrdersAction.hpp; sourceTree = ""; }; 2AAFD7F9220DD2DC002461A4 /* TrackPlaceAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TrackPlaceAction.hpp; sourceTree = ""; }; @@ -2027,6 +2029,7 @@ isa = PBXGroup; children = ( 2A61CAFA2229E5C50095AD67 /* RideEntranceExitPlaceAction.hpp */, + 2A61CAF82229E59F0095AD67 /* WaterSetHeightAction.hpp */, 2ACBAB162226850A0034FB91 /* RideSetSetting.hpp */, 2A43D2B92225B8D900E8F73B /* LoadOrQuitAction.hpp */, 2A43D2B72225B8D900E8F73B /* RideSetVehiclesAction.hpp */, @@ -3347,6 +3350,7 @@ C6352B971F477032006CCEE3 /* SetParkEntranceFeeAction.hpp in Headers */, 2AA050332209A8E300D3A922 /* StaffSetOrdersAction.hpp in Headers */, 2AAFD7FA220DD2DC002461A4 /* TrackPlaceAction.hpp in Headers */, + 2A61CAF92229E59F0095AD67 /* WaterSetHeightAction.hpp in Headers */, 933F2CBB20935668001B33FD /* LocalisationService.h in Headers */, 2A5C1368221E9F9000F8C245 /* TrackRemoveAction.hpp in Headers */, 2A61CAFB2229E5C50095AD67 /* RideEntranceExitPlaceAction.hpp in Headers */, diff --git a/src/openrct2/Game.cpp b/src/openrct2/Game.cpp index 9f30610492..cd9885e30a 100644 --- a/src/openrct2/Game.cpp +++ b/src/openrct2/Game.cpp @@ -1276,7 +1276,7 @@ GAME_COMMAND_POINTER* new_game_command_table[GAME_COMMAND_COUNT] = { nullptr, nullptr, nullptr, - game_command_set_water_height, + nullptr, game_command_place_footpath, game_command_place_footpath_from_track, nullptr, diff --git a/src/openrct2/Game.h b/src/openrct2/Game.h index 0745b7a184..3d210d9289 100644 --- a/src/openrct2/Game.h +++ b/src/openrct2/Game.h @@ -34,7 +34,7 @@ enum GAME_COMMAND GAME_COMMAND_REMOVE_RIDE_ENTRANCE_OR_EXIT, // GA GAME_COMMAND_REMOVE_SCENERY, // GA GAME_COMMAND_PLACE_SCENERY, // GA - GAME_COMMAND_SET_WATER_HEIGHT, + GAME_COMMAND_SET_WATER_HEIGHT, // GA GAME_COMMAND_PLACE_PATH, GAME_COMMAND_PLACE_PATH_FROM_TRACK, GAME_COMMAND_REMOVE_PATH, diff --git a/src/openrct2/actions/GameActionRegistration.cpp b/src/openrct2/actions/GameActionRegistration.cpp index db6a21e0c6..f99e9a3e07 100644 --- a/src/openrct2/actions/GameActionRegistration.cpp +++ b/src/openrct2/actions/GameActionRegistration.cpp @@ -47,6 +47,7 @@ #include "TrackPlaceAction.hpp" #include "TrackRemoveAction.hpp" #include "WallRemoveAction.hpp" +#include "WaterSetHeightAction.hpp" namespace GameActions { @@ -91,5 +92,6 @@ namespace GameActions Register(); Register(); Register(); + Register(); } } // namespace GameActions diff --git a/src/openrct2/actions/WaterSetHeightAction.hpp b/src/openrct2/actions/WaterSetHeightAction.hpp new file mode 100644 index 0000000000..e7b76ae133 --- /dev/null +++ b/src/openrct2/actions/WaterSetHeightAction.hpp @@ -0,0 +1,163 @@ +/***************************************************************************** + * 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 "../OpenRCT2.h" +#include "../management/Finance.h" +#include "../world/Park.h" +#include "../world/Surface.h" +#include "GameAction.h" + +DEFINE_GAME_ACTION(WaterSetHeightAction, GAME_COMMAND_SET_WATER_HEIGHT, GameActionResult) +{ +private: + CoordsXY _coords; + uint8_t _height; + +public: + WaterSetHeightAction() + { + } + WaterSetHeightAction(CoordsXY coords, uint8_t height) + : _coords(coords) + , _height(height) + { + } + + uint16_t GetActionFlags() const override + { + return GameAction::GetActionFlags(); + } + + void Serialise(DataSerialiser & stream) override + { + GameAction::Serialise(stream); + + stream << DS_TAG(_coords) << DS_TAG(_height); + } + + GameActionResult::Ptr Query() const override + { + auto res = MakeResult(); + res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; + res->Position.x = _coords.x + 16; + res->Position.y = _coords.y + 16; + res->Position.z = _height * 8; + + if (!(gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && !gCheatsSandboxMode + && gParkFlags & PARK_FLAGS_FORBID_LANDSCAPE_CHANGES) + { + return MakeResult(GA_ERROR::DISALLOWED, STR_NONE, STR_FORBIDDEN_BY_THE_LOCAL_AUTHORITY); + } + + rct_string_id errorMsg = CheckParameters(); + if (errorMsg != STR_NONE) + { + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_NONE, errorMsg); + } + + if (!(gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && !gCheatsSandboxMode) + { + if (!map_is_location_in_park(_coords)) + { + return MakeResult(GA_ERROR::DISALLOWED, STR_NONE, STR_LAND_NOT_OWNED_BY_PARK); + } + } + + SurfaceElement* surfaceElement = map_get_surface_element_at(_coords)->AsSurface(); + if (surfaceElement == nullptr) + { + log_error("Could not find surface element at: x %u, y %u", _coords.x, _coords.y); + return MakeResult(GA_ERROR::UNKNOWN, STR_NONE); + } + + int32_t zHigh = surfaceElement->base_height; + int32_t zLow = _height; + if (surfaceElement->GetWaterHeight() > 0) + { + zHigh = surfaceElement->GetWaterHeight() * 2; + } + if (zLow > zHigh) + { + int32_t temp = zHigh; + zHigh = zLow; + zLow = temp; + } + + if (!map_can_construct_at(_coords.x, _coords.y, zLow, zHigh, { 0b1111, 0b1111 })) + { + return MakeResult(GA_ERROR::NO_CLEARANCE, STR_NONE, gGameCommandErrorText, gCommonFormatArgs); + } + if (surfaceElement->HasTrackThatNeedsWater()) + { + return MakeResult(GA_ERROR::DISALLOWED, STR_NONE); + } + + res->Cost = 250; + + return res; + } + + GameActionResult::Ptr Execute() const override + { + auto res = MakeResult(); + res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; + res->Position.x = _coords.x + 16; + res->Position.y = _coords.y + 16; + res->Position.z = _height * 8; + + int32_t surfaceHeight = tile_element_height(_coords.x, _coords.y) & 0xFFFF; + footpath_remove_litter(_coords.x, _coords.y, surfaceHeight); + if (!gCheatsDisableClearanceChecks) + wall_remove_at_z(_coords.x, _coords.y, surfaceHeight); + + SurfaceElement* surfaceElement = map_get_surface_element_at(_coords)->AsSurface(); + if (surfaceElement == nullptr) + { + log_error("Could not find surface element at: x %u, y %u", _coords.x, _coords.y); + return std::make_unique(GA_ERROR::UNKNOWN, STR_NONE); + } + + if (_height > surfaceElement->base_height) + { + surfaceElement->SetWaterHeight(_height / 2); + } + else + { + surfaceElement->SetWaterHeight(0); + } + map_invalidate_tile_full(_coords.x, _coords.y); + + res->Cost = 250; + + return res; + } + +private: + rct_string_id CheckParameters() const + { + if (_coords.x > gMapSizeMaxXY || _coords.y > gMapSizeMaxXY) + { + return STR_OFF_EDGE_OF_MAP; + } + + if (_height < MINIMUM_WATER_HEIGHT) + { + return STR_TOO_LOW; + } + + if (_height > MAXIMUM_WATER_HEIGHT) + { + return STR_TOO_HIGH; + } + + return STR_NONE; + } +}; diff --git a/src/openrct2/network/Network.cpp b/src/openrct2/network/Network.cpp index e303210a27..893ef90045 100644 --- a/src/openrct2/network/Network.cpp +++ b/src/openrct2/network/Network.cpp @@ -31,7 +31,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 "51" +#define NETWORK_STREAM_VERSION "52" #define NETWORK_STREAM_ID OPENRCT2_VERSION "-" NETWORK_STREAM_VERSION static Peep* _pickup_peep = nullptr; diff --git a/src/openrct2/world/Map.cpp b/src/openrct2/world/Map.cpp index 5bf41a46d7..b5b9fda256 100644 --- a/src/openrct2/world/Map.cpp +++ b/src/openrct2/world/Map.cpp @@ -19,6 +19,7 @@ #include "../actions/LargeSceneryRemoveAction.hpp" #include "../actions/SmallSceneryRemoveAction.hpp" #include "../actions/WallRemoveAction.hpp" +#include "../actions/WaterSetHeightAction.hpp" #include "../audio/audio.h" #include "../config/Config.h" #include "../core/Guard.hpp" @@ -1402,12 +1403,18 @@ money32 raise_water(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint8_t flag height = tile_element->base_height + 2; } - money32 tileCost = game_do_command( - xi, flags, yi, (max_height << 8) + height, GAME_COMMAND_SET_WATER_HEIGHT, 0, 0); - if (tileCost == MONEY32_UNDEFINED) + auto waterSetHeightAction = WaterSetHeightAction({ xi, yi }, height); + waterSetHeightAction.SetFlags(flags); + auto res = flags & GAME_COMMAND_FLAG_APPLY ? GameActions::ExecuteNested(&waterSetHeightAction) + : GameActions::QueryNested(&waterSetHeightAction); + if (res->Error != GA_ERROR::OK) + { + gGameCommandErrorText = res->ErrorMessage; + // set gCommonFormatArguments to res->ErrorArgs return MONEY32_UNDEFINED; + } - cost += tileCost; + cost += res->Cost; waterHeightChanged = true; } } @@ -1490,11 +1497,18 @@ money32 lower_water(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint8_t flag if (height < min_height) continue; height -= 2; - int32_t tileCost = game_do_command( - xi, flags, yi, (min_height << 8) + height, GAME_COMMAND_SET_WATER_HEIGHT, 0, 0); - if (tileCost == MONEY32_UNDEFINED) + auto waterSetHeightAction = WaterSetHeightAction({ xi, yi }, height); + waterSetHeightAction.SetFlags(flags); + auto res = flags & GAME_COMMAND_FLAG_APPLY ? GameActions::ExecuteNested(&waterSetHeightAction) + : GameActions::QueryNested(&waterSetHeightAction); + if (res->Error != GA_ERROR::OK) + { + gGameCommandErrorText = res->ErrorMessage; + // set gCommonFormatArguments to res->ErrorArgs return MONEY32_UNDEFINED; - cost += tileCost; + } + + cost += res->Cost; waterHeightChanged = true; } } @@ -2135,116 +2149,6 @@ void game_command_lower_water( (int16_t)(*eax & 0xFFFF), (int16_t)(*ecx & 0xFFFF), (int16_t)(*edi & 0xFFFF), (int16_t)(*ebp & 0xFFFF), (uint8_t)*ebx); } -/** - * - * rct2: 0x006E650F - */ -void game_command_set_water_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) -{ - int32_t x = *eax; - int32_t y = *ecx; - uint8_t base_height = *edx; - gCommandExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; - gCommandPosition.x = x + 16; - gCommandPosition.y = y + 16; - gCommandPosition.z = base_height * 8; - if (game_is_paused() && !gCheatsBuildInPauseMode) - { - gGameCommandErrorText = STR_CONSTRUCTION_NOT_POSSIBLE_WHILE_GAME_IS_PAUSED; - *ebx = MONEY32_UNDEFINED; - return; - } - if (!(gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && !gCheatsSandboxMode - && gParkFlags & PARK_FLAGS_FORBID_LANDSCAPE_CHANGES) - { - gGameCommandErrorText = STR_FORBIDDEN_BY_THE_LOCAL_AUTHORITY; - *ebx = MONEY32_UNDEFINED; - return; - } - - if (base_height < 2) - { - gGameCommandErrorText = STR_TOO_LOW; - *ebx = MONEY32_UNDEFINED; - return; - } - - if (base_height >= 58) - { - gGameCommandErrorText = STR_TOO_HIGH; - *ebx = MONEY32_UNDEFINED; - return; - } - - if (x >= gMapSizeUnits || y >= gMapSizeUnits) - { - gGameCommandErrorText = STR_OFF_EDGE_OF_MAP; - *ebx = MONEY32_UNDEFINED; - return; - } - - if (!(gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && !gCheatsSandboxMode && !map_is_location_in_park({ x, y })) - { - *ebx = MONEY32_UNDEFINED; - return; - } - - if (*ebx & GAME_COMMAND_FLAG_APPLY) - { - int32_t element_height = tile_element_height(x, y); - footpath_remove_litter(x, y, element_height); - if (!gCheatsDisableClearanceChecks) - wall_remove_at_z(x, y, element_height); - } - - TileElement* tile_element = map_get_surface_element_at({ x, y }); - int32_t zHigh = tile_element->base_height; - int32_t zLow = base_height; - if (tile_element->AsSurface()->GetWaterHeight() > 0) - { - zHigh = tile_element->AsSurface()->GetWaterHeight() * 2; - } - if (zLow > zHigh) - { - int32_t temp = zHigh; - zHigh = zLow; - zLow = temp; - } - - if (map_can_construct_at(x, y, zLow, zHigh, { 0b1111, 0b1111 })) - { - if (tile_element->AsSurface()->HasTrackThatNeedsWater()) - { - gGameCommandErrorText = 0; - *ebx = MONEY32_UNDEFINED; - return; - } - if (*ebx & GAME_COMMAND_FLAG_APPLY) - { - if (base_height > tile_element->base_height) - { - tile_element->AsSurface()->SetWaterHeight(base_height / 2); - } - else - { - tile_element->AsSurface()->SetWaterHeight(0); - } - map_invalidate_tile_full(x, y); - } - *ebx = 250; - if (gParkFlags & PARK_FLAGS_NO_MONEY) - { - *ebx = 0; - } - } - else - { - *ebx = MONEY32_UNDEFINED; - } -} - bool map_is_location_at_edge(int32_t x, int32_t y) { return x < 32 || y < 32 || x >= ((MAXIMUM_MAP_SIZE_TECHNICAL - 1) * 32) || y >= ((MAXIMUM_MAP_SIZE_TECHNICAL - 1) * 32); diff --git a/src/openrct2/world/Map.h b/src/openrct2/world/Map.h index 454774548a..5bf2570356 100644 --- a/src/openrct2/world/Map.h +++ b/src/openrct2/world/Map.h @@ -19,6 +19,8 @@ #define MINIMUM_LAND_HEIGHT 2 #define MAXIMUM_LAND_HEIGHT 142 +#define MINIMUM_WATER_HEIGHT 2 +#define MAXIMUM_WATER_HEIGHT 58 #define MINIMUM_MAP_SIZE_TECHNICAL 15 #define MAXIMUM_MAP_SIZE_TECHNICAL 256 @@ -202,8 +204,6 @@ void game_command_lower_land(int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* void game_command_smooth_land(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_water(int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp); void game_command_lower_water(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_water_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_place_banner( int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp); void game_command_place_wall(int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp);