diff --git a/src/openrct2/actions/GameActionRegistration.cpp b/src/openrct2/actions/GameActionRegistration.cpp index 2caa6cdff5..3de9dad348 100644 --- a/src/openrct2/actions/GameActionRegistration.cpp +++ b/src/openrct2/actions/GameActionRegistration.cpp @@ -33,6 +33,7 @@ #include "LargeSceneryRemoveAction.hpp" #include "LargeScenerySetColourAction.hpp" #include "LoadOrQuitAction.hpp" +#include "MazePlaceTrackAction.hpp" #include "MazeSetTrackAction.hpp" #include "NetworkModifyGroupAction.hpp" #include "ParkEntranceRemoveAction.hpp" @@ -103,6 +104,7 @@ namespace GameActions Register(); Register(); Register(); + Register(); Register(); Register(); Register(); diff --git a/src/openrct2/actions/MazePlaceTrackAction.hpp b/src/openrct2/actions/MazePlaceTrackAction.hpp new file mode 100644 index 0000000000..f2f53d1bd5 --- /dev/null +++ b/src/openrct2/actions/MazePlaceTrackAction.hpp @@ -0,0 +1,209 @@ +/***************************************************************************** + * 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 "../management/Finance.h" +#include "../ride/RideData.h" +#include "../ride/TrackData.h" +#include "GameAction.h" + + +DEFINE_GAME_ACTION(MazePlaceTrackAction, GAME_COMMAND_PLACE_MAZE_DESIGN, GameActionResult) +{ +private: + CoordsXYZ _loc; + NetworkRideId_t _rideIndex{ RIDE_ID_NULL }; + uint16_t _mazeEntry{ 0 }; + +public: + MazePlaceTrackAction() + { + } + MazePlaceTrackAction(CoordsXYZ location, NetworkRideId_t rideIndex, uint16_t mazeEntry) + : _loc(location) + , _rideIndex(rideIndex) + , _mazeEntry(mazeEntry) + { + } + + void Serialise(DataSerialiser & stream) override + { + GameAction::Serialise(stream); + stream << DS_TAG(_loc) << DS_TAG(_rideIndex) << DS_TAG(_mazeEntry); + } + + GameActionResult::Ptr Query() const override + { + auto res = std::make_unique(); + + res->Position.x = _loc.x + 8; + res->Position.y = _loc.y + 8; + res->Position.z = _loc.z; + res->ExpenditureType = RCT_EXPENDITURE_TYPE_RIDE_CONSTRUCTION; + res->ErrorTitle = STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE; + if (!map_check_free_elements_and_reorganise(1)) + { + res->Error = GA_ERROR::NO_FREE_ELEMENTS; + res->ErrorMessage = STR_TILE_ELEMENT_LIMIT_REACHED; + return res; + } + if ((_loc.z & 0xF) != 0) + { + res->Error = GA_ERROR::UNKNOWN; + res->ErrorMessage = STR_CONSTRUCTION_ERR_UNKNOWN; + return res; + } + + if (!map_is_location_owned(_loc) && !gCheatsSandboxMode) + { + res->Error = GA_ERROR::NOT_OWNED; + res->ErrorMessage = STR_LAND_NOT_OWNED_BY_PARK; + return res; + } + + auto surfaceElement = map_get_surface_element_at(_loc); + if (surfaceElement == nullptr) + { + res->Error = GA_ERROR::UNKNOWN; + res->ErrorMessage = STR_INVALID_SELECTION_OF_OBJECTS; + return res; + } + + uint8_t baseHeight = _loc.z / 8; + uint8_t clearanceHeight = (_loc.z + 32) / 8; + + int8_t heightDifference = baseHeight - surfaceElement->base_height; + if (heightDifference >= 0 && !gCheatsDisableSupportLimits) + { + heightDifference = heightDifference >> 1; + + if (heightDifference > RideData5[RIDE_TYPE_MAZE].max_height) + { + res->Error = GA_ERROR::TOO_HIGH; + res->ErrorMessage = STR_TOO_HIGH_FOR_SUPPORTS; + return res; + } + } + + money32 clearCost = 0; + + if (!map_can_construct_with_clear_at( + floor2(_loc.x, 32), floor2(_loc.y, 32), baseHeight, clearanceHeight, &map_place_non_scenery_clear_func, + { 0b1111, 0 }, GetFlags(), &clearCost, CREATE_CROSSING_MODE_NONE)) + { + return MakeResult(GA_ERROR::NO_CLEARANCE, res->ErrorTitle, gGameCommandErrorText, gCommonFormatArgs); + } + + if (gMapGroundFlags & ELEMENT_IS_UNDERWATER) + { + res->Error = GA_ERROR::NO_CLEARANCE; + res->ErrorMessage = STR_RIDE_CANT_BUILD_THIS_UNDERWATER; + return res; + } + + if (gMapGroundFlags & ELEMENT_IS_UNDERGROUND) + { + res->Error = GA_ERROR::NO_CLEARANCE; + res->ErrorMessage = STR_CAN_ONLY_BUILD_THIS_ABOVE_GROUND; + return res; + } + + auto ride = get_ride(_rideIndex); + if (ride == nullptr || ride->type == RIDE_TYPE_NULL) + { + res->Error = GA_ERROR::INVALID_PARAMETERS; + res->ErrorMessage = STR_INVALID_SELECTION_OF_OBJECTS; + return res; + } + + money32 price = (((RideTrackCosts[ride->type].track_price * TrackPricing[TRACK_ELEM_MAZE]) >> 16)); + res->Cost = clearCost + price / 2 * 10; + + return res; + } + + GameActionResult::Ptr Execute() const override + { + auto res = std::make_unique(); + + res->Position.x = _loc.x + 8; + res->Position.y = _loc.y + 8; + res->Position.z = _loc.z; + res->ExpenditureType = RCT_EXPENDITURE_TYPE_RIDE_CONSTRUCTION; + res->ErrorTitle = STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE; + + auto ride = get_ride(_rideIndex); + if (ride == nullptr) + { + res->Error = GA_ERROR::INVALID_PARAMETERS; + res->ErrorMessage = STR_NONE; + return res; + } + + if (!map_check_free_elements_and_reorganise(1)) + { + res->Error = GA_ERROR::NO_FREE_ELEMENTS; + res->ErrorMessage = STR_NONE; + return res; + } + + uint32_t flags = GetFlags(); + if (!(flags & GAME_COMMAND_FLAG_GHOST)) + { + footpath_remove_litter(_loc.x, _loc.y, _loc.z); + wall_remove_at(floor2(_loc.x, 32), floor2(_loc.y, 32), _loc.z, _loc.z + 32); + } + + uint8_t baseHeight = _loc.z / 8; + uint8_t clearanceHeight = (_loc.z + 32) / 8; + + money32 clearCost = 0; + if (!map_can_construct_with_clear_at( + floor2(_loc.x, 32), floor2(_loc.y, 32), baseHeight, clearanceHeight, &map_place_non_scenery_clear_func, + { 0b1111, 0 }, GetFlags() | GAME_COMMAND_FLAG_APPLY, &clearCost, CREATE_CROSSING_MODE_NONE)) + { + return MakeResult(GA_ERROR::NO_CLEARANCE, res->ErrorTitle, gGameCommandErrorText, gCommonFormatArgs); + } + + money32 price = (((RideTrackCosts[ride->type].track_price * TrackPricing[TRACK_ELEM_MAZE]) >> 16)); + res->Cost = clearCost + price / 2 * 10; + + uint16_t flooredX = floor2(_loc.x, 32); + uint16_t flooredY = floor2(_loc.y, 32); + + auto tileElement = tile_element_insert({ _loc.x / 32, _loc.y / 32, baseHeight }, 0b1111); + assert(tileElement != nullptr); + + tileElement->clearance_height = clearanceHeight + 4; + tileElement->SetType(TILE_ELEMENT_TYPE_TRACK); + + tileElement->AsTrack()->SetTrackType(TRACK_ELEM_MAZE); + tileElement->AsTrack()->SetRideIndex(_rideIndex); + tileElement->AsTrack()->SetMazeEntry(_mazeEntry); + + if (flags & GAME_COMMAND_FLAG_GHOST) + { + tileElement->SetGhost(true); + } + + map_invalidate_tile_full(flooredX, flooredY); + + ride->maze_tiles++; + ride->stations[0].Height = tileElement->base_height; + ride->stations[0].Start.xy = 0; + + if (ride->maze_tiles == 1) + { + ride->overall_view.x = flooredX / 32; + ride->overall_view.y = flooredY / 32; + } + + return res; + } +}; diff --git a/src/openrct2/ride/TrackDesign.cpp b/src/openrct2/ride/TrackDesign.cpp index 7a6c22ea33..85bbbf3c4b 100644 --- a/src/openrct2/ride/TrackDesign.cpp +++ b/src/openrct2/ride/TrackDesign.cpp @@ -13,6 +13,7 @@ #include "../Game.h" #include "../OpenRCT2.h" #include "../TrackImporter.h" +#include "../actions/MazePlaceTrackAction.hpp" #include "../actions/FootpathPlaceFromTrackAction.hpp" #include "../actions/FootpathRemoveAction.hpp" #include "../actions/LargeSceneryPlaceAction.hpp" @@ -1353,9 +1354,11 @@ static int32_t track_design_place_maze(TrackDesign* td6, int16_t x, int16_t y, i gGameCommandErrorTitle = STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE; - cost = game_do_command( - mapCoord.x, flags | (maze_entry & 0xFF) << 8, mapCoord.y, ride->id | (maze_entry & 0xFF00), - GAME_COMMAND_PLACE_MAZE_DESIGN, z, 0); + auto mazePlace = MazePlaceTrackAction({ mapCoord, z }, ride->id, maze_entry); + mazePlace.SetFlags(flags); + auto res = flags & GAME_COMMAND_FLAG_APPLY ? GameActions::ExecuteNested(&mazePlace) + : GameActions::QueryNested(&mazePlace); + cost = res->Error == GA_ERROR::OK ? res->Cost : MONEY32_UNDEFINED; break; }