diff --git a/contributors.md b/contributors.md index e091a8a6a2..f900f66381 100644 --- a/contributors.md +++ b/contributors.md @@ -127,6 +127,8 @@ The following people are not part of the development team, but have been contrib * Zetao Ye (ZbrettonYe) * Jordan Arevalos (Jarevalos2017) * Florian Will (w-flo) +* Trevor Harkness (tharkne) +* Steve Xu (stevexu-umich) ## Toolchain * (Balletie) - macOS diff --git a/src/openrct2-ui/windows/TopToolbar.cpp b/src/openrct2-ui/windows/TopToolbar.cpp index 6cd585c95a..4faf5d81e5 100644 --- a/src/openrct2-ui/windows/TopToolbar.cpp +++ b/src/openrct2-ui/windows/TopToolbar.cpp @@ -2487,7 +2487,7 @@ static money32 try_place_ghost_scenery( tileElement = gSceneryTileElement; gSceneryGhostPosition.z = tileElement->base_height; gSceneryQuadrant = tileElement->AsSmallScenery()->GetSceneryQuadrant(); - if (gSceneryGroundFlags & ELEMENT_IS_UNDERGROUND) + if (dynamic_cast(res.get())->GroundFlags & ELEMENT_IS_UNDERGROUND) { // Set underground on viewport_set_visibility(4); @@ -2582,7 +2582,7 @@ static money32 try_place_ghost_scenery( tileElement = gSceneryTileElement; gSceneryGhostPosition.z = tileElement->base_height; - if (gSceneryGroundFlags & ELEMENT_IS_UNDERGROUND) + if (dynamic_cast(res.get())->GroundFlags & ELEMENT_IS_UNDERGROUND) { // Set underground on viewport_set_visibility(4); diff --git a/src/openrct2/actions/LargeSceneryPlaceAction.hpp b/src/openrct2/actions/LargeSceneryPlaceAction.hpp index 60796a24ca..9baf393df3 100644 --- a/src/openrct2/actions/LargeSceneryPlaceAction.hpp +++ b/src/openrct2/actions/LargeSceneryPlaceAction.hpp @@ -19,7 +19,30 @@ #include "../world/Scenery.h" #include "GameAction.h" -DEFINE_GAME_ACTION(LargeSceneryPlaceAction, GAME_COMMAND_PLACE_LARGE_SCENERY, GameActionResult) +class LargeSceneryPlaceActionResult final : public GameActionResult +{ +public: + LargeSceneryPlaceActionResult() + : GameActionResult(GA_ERROR::OK, STR_CANT_POSITION_THIS_HERE) + { + } + LargeSceneryPlaceActionResult(GA_ERROR error) + : GameActionResult(error, STR_CANT_POSITION_THIS_HERE) + { + } + LargeSceneryPlaceActionResult(GA_ERROR error, rct_string_id message) + : GameActionResult(error, STR_CANT_POSITION_THIS_HERE, message) + { + } + LargeSceneryPlaceActionResult(GA_ERROR error, rct_string_id message, uint8_t* args) + : GameActionResult(error, STR_CANT_POSITION_THIS_HERE, message, args) + { + } + + uint8_t GroundFlags{ 0 }; +}; + +DEFINE_GAME_ACTION(LargeSceneryPlaceAction, GAME_COMMAND_PLACE_LARGE_SCENERY, LargeSceneryPlaceActionResult) { private: CoordsXYZD _loc; @@ -62,15 +85,15 @@ public: GameActionResult::Ptr Query() const override { - auto res = MakeResult(); + auto res = std::make_unique(); res->ErrorTitle = STR_CANT_POSITION_THIS_HERE; res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; int16_t surfaceHeight = tile_element_height(_loc.x, _loc.y) & 0xFFFF; res->Position.x = _loc.x + 16; res->Position.y = _loc.y + 16; res->Position.z = surfaceHeight; + res->GroundFlags = 0; - gSceneryGroundFlags = 0; money32 supportsCost = 0; if (_primaryColour > TILE_ELEMENT_COLOUR_MASK || _secondaryColour > TILE_ELEMENT_COLOUR_MASK) @@ -78,20 +101,20 @@ public: log_error( "Invalid game command for scenery placement, primaryColour = %u, secondaryColour = %u", _primaryColour, _secondaryColour); - return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_POSITION_THIS_HERE); + return std::make_unique(GA_ERROR::INVALID_PARAMETERS); } if (_sceneryType >= MAX_LARGE_SCENERY_OBJECTS) { log_error("Invalid game command for scenery placement, sceneryType = %u", _sceneryType); - return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_POSITION_THIS_HERE); + return std::make_unique(GA_ERROR::INVALID_PARAMETERS); } rct_scenery_entry* sceneryEntry = get_large_scenery_entry(_sceneryType); if (sceneryEntry == nullptr) { log_error("Invalid game command for scenery placement, sceneryType = %u", _sceneryType); - return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_POSITION_THIS_HERE); + return std::make_unique(GA_ERROR::INVALID_PARAMETERS); } uint32_t totalNumTiles = GetTotalNumTiles(sceneryEntry->large_scenery.tiles); @@ -109,20 +132,20 @@ public: if (_bannerId == BANNER_INDEX_NULL) { log_error("Banner Index not specified."); - return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_TOO_MANY_BANNERS_IN_GAME, STR_CANT_POSITION_THIS_HERE); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_TOO_MANY_BANNERS_IN_GAME); } if (gBanners[_bannerId].type != BANNER_NULL) { log_error("No free banners available"); - return MakeResult(GA_ERROR::NO_FREE_ELEMENTS, STR_CANT_POSITION_THIS_HERE); + return std::make_unique(GA_ERROR::NO_FREE_ELEMENTS); } } if (!map_check_free_elements_and_reorganise(totalNumTiles)) { log_error("No free map elements available"); - return MakeResult(GA_ERROR::NO_FREE_ELEMENTS, STR_CANT_POSITION_THIS_HERE); + return std::make_unique(GA_ERROR::NO_FREE_ELEMENTS); } uint8_t tileNum = 0; @@ -144,8 +167,8 @@ public: curTile.x, curTile.y, zLow, zHigh, &map_place_scenery_clear_func, quarterTile, GetFlags(), &supportsCost, CREATE_CROSSING_MODE_NONE)) { - return MakeResult( - GA_ERROR::NO_CLEARANCE, STR_CANT_POSITION_THIS_HERE, gGameCommandErrorText, gCommonFormatArgs); + return std::make_unique( + GA_ERROR::NO_CLEARANCE, gGameCommandErrorText, gCommonFormatArgs); } int32_t tempSceneryGroundFlags = gMapGroundFlags & (ELEMENT_IS_ABOVE_GROUND | ELEMENT_IS_UNDERGROUND); @@ -153,25 +176,27 @@ public: { if ((gMapGroundFlags & ELEMENT_IS_UNDERWATER) || (gMapGroundFlags & ELEMENT_IS_UNDERGROUND)) { - return MakeResult(GA_ERROR::DISALLOWED, STR_CANT_POSITION_THIS_HERE, STR_CANT_BUILD_THIS_UNDERWATER); + return std::make_unique( + GA_ERROR::DISALLOWED, STR_CANT_BUILD_THIS_UNDERWATER); } - if (gSceneryGroundFlags && !(gSceneryGroundFlags & tempSceneryGroundFlags)) + if (res->GroundFlags && !(res->GroundFlags & tempSceneryGroundFlags)) { - return MakeResult( - GA_ERROR::DISALLOWED, STR_CANT_POSITION_THIS_HERE, STR_CANT_BUILD_PARTLY_ABOVE_AND_PARTLY_BELOW_GROUND); + return std::make_unique( + GA_ERROR::DISALLOWED, STR_CANT_BUILD_PARTLY_ABOVE_AND_PARTLY_BELOW_GROUND); } } - gSceneryGroundFlags = tempSceneryGroundFlags; + + res->GroundFlags = tempSceneryGroundFlags; if (curTile.x >= gMapSizeUnits || curTile.y >= gMapSizeUnits) { - return MakeResult(GA_ERROR::DISALLOWED, STR_CANT_POSITION_THIS_HERE, STR_OFF_EDGE_OF_MAP); + return std::make_unique(GA_ERROR::DISALLOWED, STR_OFF_EDGE_OF_MAP); } if (!(gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && !map_is_location_owned(curTile.x, curTile.y, zLow * 8) && !gCheatsSandboxMode) { - return MakeResult(GA_ERROR::DISALLOWED, STR_CANT_POSITION_THIS_HERE, STR_LAND_NOT_OWNED_BY_PARK); + return std::make_unique(GA_ERROR::DISALLOWED, STR_LAND_NOT_OWNED_BY_PARK); } } @@ -184,28 +209,28 @@ public: GameActionResult::Ptr Execute() const override { - auto res = MakeResult(); + auto res = std::make_unique(); res->ErrorTitle = STR_CANT_POSITION_THIS_HERE; int16_t surfaceHeight = tile_element_height(_loc.x, _loc.y) & 0xFFFF; res->Position.x = _loc.x + 16; res->Position.y = _loc.y + 16; res->Position.z = surfaceHeight; + res->GroundFlags = 0; - gSceneryGroundFlags = 0; money32 supportsCost = 0; rct_scenery_entry* sceneryEntry = get_large_scenery_entry(_sceneryType); if (sceneryEntry == nullptr) { log_error("Invalid game command for scenery placement, sceneryType = %u", _sceneryType); - return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_POSITION_THIS_HERE); + return std::make_unique(GA_ERROR::INVALID_PARAMETERS); } if (sceneryEntry->large_scenery.tiles == nullptr) { log_error("Invalid large scenery object, sceneryType = %u", _sceneryType); - return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_POSITION_THIS_HERE); + return std::make_unique(GA_ERROR::INVALID_PARAMETERS); } uint32_t totalNumTiles = GetTotalNumTiles(sceneryEntry->large_scenery.tiles); @@ -223,13 +248,13 @@ public: if (_bannerId == BANNER_INDEX_NULL) { log_error("No free banners available"); - return MakeResult(GA_ERROR::NO_FREE_ELEMENTS, STR_TOO_MANY_BANNERS_IN_GAME, STR_CANT_POSITION_THIS_HERE); + return MakeResult(GA_ERROR::NO_FREE_ELEMENTS, STR_TOO_MANY_BANNERS_IN_GAME); } if (gBanners[_bannerId].type != BANNER_NULL) { log_error("No free banners available"); - return MakeResult(GA_ERROR::NO_FREE_ELEMENTS, STR_CANT_POSITION_THIS_HERE); + return std::make_unique(GA_ERROR::NO_FREE_ELEMENTS); } rct_banner* banner = &gBanners[_bannerId]; @@ -252,7 +277,7 @@ public: if (!map_check_free_elements_and_reorganise(totalNumTiles)) { log_error("No free map elements available"); - return MakeResult(GA_ERROR::NO_FREE_ELEMENTS, STR_CANT_POSITION_THIS_HERE); + return std::make_unique(GA_ERROR::NO_FREE_ELEMENTS); } uint8_t tileNum = 0; @@ -274,11 +299,11 @@ public: curTile.x, curTile.y, zLow, zHigh, &map_place_scenery_clear_func, quarterTile, GetFlags(), &supportsCost, CREATE_CROSSING_MODE_NONE)) { - return MakeResult( - GA_ERROR::NO_CLEARANCE, STR_CANT_POSITION_THIS_HERE, gGameCommandErrorText, gCommonFormatArgs); + return std::make_unique( + GA_ERROR::NO_CLEARANCE, gGameCommandErrorText, gCommonFormatArgs); } - gSceneryGroundFlags = gMapGroundFlags & (ELEMENT_IS_ABOVE_GROUND | ELEMENT_IS_UNDERGROUND); + res->GroundFlags = gMapGroundFlags & (ELEMENT_IS_ABOVE_GROUND | ELEMENT_IS_UNDERGROUND); if (!(GetFlags() & GAME_COMMAND_FLAG_GHOST)) { @@ -297,7 +322,7 @@ public: newTileElement->clearance_height = zHigh; auto newSceneryElement = newTileElement->AsLargeScenery(); - SetNewLargeSceneryElement(*newSceneryElement, tileNum, _bannerId); + SetNewLargeSceneryElement(*newSceneryElement, tileNum); if (tileNum == 0) { @@ -367,7 +392,7 @@ private: return maxHeight; } - void SetNewLargeSceneryElement(LargeSceneryElement & sceneryElement, uint8_t tileNum, BannerIndex bannerId) const + void SetNewLargeSceneryElement(LargeSceneryElement & sceneryElement, uint8_t tileNum) const { sceneryElement.SetDirection(_loc.direction); sceneryElement.SetEntryIndex(_sceneryType); @@ -375,9 +400,9 @@ private: sceneryElement.SetPrimaryColour(_primaryColour); sceneryElement.SetSecondaryColour(_secondaryColour); - if (bannerId != BANNER_INDEX_NULL) + if (_bannerId != BANNER_INDEX_NULL) { - sceneryElement.SetBannerIndex(bannerId); + sceneryElement.SetBannerIndex(_bannerId); } if (GetFlags() & GAME_COMMAND_FLAG_GHOST) diff --git a/src/openrct2/actions/SmallSceneryPlaceAction.hpp b/src/openrct2/actions/SmallSceneryPlaceAction.hpp index f20db46d2d..131cce2bbd 100644 --- a/src/openrct2/actions/SmallSceneryPlaceAction.hpp +++ b/src/openrct2/actions/SmallSceneryPlaceAction.hpp @@ -27,7 +27,30 @@ #include "../world/TileElement.h" #include "GameAction.h" -DEFINE_GAME_ACTION(SmallSceneryPlaceAction, GAME_COMMAND_PLACE_SCENERY, GameActionResult) +class SmallSceneryPlaceActionResult final : public GameActionResult +{ +public: + SmallSceneryPlaceActionResult() + : GameActionResult(GA_ERROR::OK, STR_CANT_POSITION_THIS_HERE) + { + } + SmallSceneryPlaceActionResult(GA_ERROR error) + : GameActionResult(error, STR_CANT_POSITION_THIS_HERE) + { + } + SmallSceneryPlaceActionResult(GA_ERROR error, rct_string_id message) + : GameActionResult(error, STR_CANT_POSITION_THIS_HERE, message) + { + } + SmallSceneryPlaceActionResult(GA_ERROR error, rct_string_id message, uint8_t* args) + : GameActionResult(error, STR_CANT_POSITION_THIS_HERE, message, args) + { + } + + uint8_t GroundFlags{ 0 }; +}; + +DEFINE_GAME_ACTION(SmallSceneryPlaceAction, GAME_COMMAND_PLACE_SCENERY, SmallSceneryPlaceActionResult) { private: CoordsXYZD _loc; @@ -81,7 +104,7 @@ public: { baseHeight >>= 16; } - auto res = MakeResult(); + auto res = std::make_unique(); res->Position.x = _loc.x + 16; res->Position.y = _loc.y + 16; res->Position.z = baseHeight; @@ -93,18 +116,18 @@ public: if (!map_check_free_elements_and_reorganise(1)) { - return MakeResult(GA_ERROR::NO_FREE_ELEMENTS, STR_CANT_POSITION_THIS_HERE); + return std::make_unique(GA_ERROR::NO_FREE_ELEMENTS); } if (!byte_9D8150 && (_loc.x > gMapSizeMaxXY || _loc.y > gMapSizeMaxXY)) { - return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_POSITION_THIS_HERE); + return std::make_unique(GA_ERROR::INVALID_PARAMETERS); } rct_scenery_entry* sceneryEntry = get_small_scenery_entry(_sceneryType); if (sceneryEntry == nullptr) { - return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_POSITION_THIS_HERE); + return std::make_unique(GA_ERROR::INVALID_PARAMETERS); } auto quadrant = _quadrant; @@ -152,7 +175,7 @@ public: if (!(gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && !gCheatsSandboxMode && !map_is_location_owned(_loc.x, _loc.y, targetHeight)) { - return MakeResult(GA_ERROR::NOT_OWNED, STR_CANT_POSITION_THIS_HERE, STR_LAND_NOT_OWNED_BY_PARK); + return std::make_unique(GA_ERROR::NOT_OWNED, STR_LAND_NOT_OWNED_BY_PARK); } TileElement* surfaceElement = map_get_surface_element_at({ _loc.x, _loc.y }); @@ -162,7 +185,7 @@ public: int32_t water_height = (surfaceElement->AsSurface()->GetWaterHeight() * 16) - 1; if (water_height > targetHeight) { - return MakeResult(GA_ERROR::DISALLOWED, STR_CANT_POSITION_THIS_HERE, STR_CANT_BUILD_THIS_UNDERWATER); + return std::make_unique(GA_ERROR::DISALLOWED, STR_CANT_BUILD_THIS_UNDERWATER); } } @@ -170,14 +193,15 @@ public: { if (isOnWater) { - return MakeResult(GA_ERROR::DISALLOWED, STR_CANT_POSITION_THIS_HERE, STR_CAN_ONLY_BUILD_THIS_ON_LAND); + return std::make_unique(GA_ERROR::DISALLOWED, STR_CAN_ONLY_BUILD_THIS_ON_LAND); } if (surfaceElement != nullptr && surfaceElement->AsSurface()->GetWaterHeight() > 0) { if (static_cast((surfaceElement->AsSurface()->GetWaterHeight() * 16)) > targetHeight) { - return MakeResult(GA_ERROR::DISALLOWED, STR_CANT_POSITION_THIS_HERE, STR_CAN_ONLY_BUILD_THIS_ON_LAND); + return std::make_unique( + GA_ERROR::DISALLOWED, STR_CAN_ONLY_BUILD_THIS_ON_LAND); } } } @@ -186,7 +210,7 @@ public: && (scenery_small_entry_has_flag(sceneryEntry, SMALL_SCENERY_FLAG_REQUIRE_FLAT_SURFACE)) && !supportsRequired && !isOnWater && surfaceElement != nullptr && (surfaceElement->AsSurface()->GetSlope() != TILE_ELEMENT_SLOPE_FLAT)) { - return MakeResult(GA_ERROR::DISALLOWED, STR_CANT_POSITION_THIS_HERE, STR_LEVEL_LAND_REQUIRED); + return std::make_unique(GA_ERROR::DISALLOWED, STR_LEVEL_LAND_REQUIRED); } if (!gCheatsDisableSupportLimits && !(scenery_small_entry_has_flag(sceneryEntry, SMALL_SCENERY_FLAG_STACKABLE)) @@ -198,13 +222,13 @@ public: { if (surfaceElement->AsSurface()->GetWaterHeight() || (surfaceElement->base_height * 8) != targetHeight) { - return MakeResult(GA_ERROR::DISALLOWED, STR_CANT_POSITION_THIS_HERE, STR_LEVEL_LAND_REQUIRED); + return std::make_unique(GA_ERROR::DISALLOWED, STR_LEVEL_LAND_REQUIRED); } } } else { - return MakeResult(GA_ERROR::DISALLOWED, STR_CANT_POSITION_THIS_HERE, STR_CAN_ONLY_BUILD_THIS_ON_LAND); + return std::make_unique(GA_ERROR::DISALLOWED, STR_CAN_ONLY_BUILD_THIS_ON_LAND); } } @@ -252,10 +276,11 @@ public: _loc.x, _loc.y, zLow, zHigh, &map_place_scenery_clear_func, quarterTile, GetFlags(), &clearCost, CREATE_CROSSING_MODE_NONE)) { - return MakeResult(GA_ERROR::DISALLOWED, STR_CANT_POSITION_THIS_HERE, gGameCommandErrorText, gCommonFormatArgs); + return std::make_unique( + GA_ERROR::DISALLOWED, gGameCommandErrorText, gCommonFormatArgs); } - gSceneryGroundFlags = gMapGroundFlags & (ELEMENT_IS_ABOVE_GROUND | ELEMENT_IS_UNDERGROUND); + res->GroundFlags = gMapGroundFlags & (ELEMENT_IS_ABOVE_GROUND | ELEMENT_IS_UNDERGROUND); res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; res->Cost = (sceneryEntry->small_scenery.price * 10) + clearCost; @@ -276,7 +301,7 @@ public: { baseHeight >>= 16; } - auto res = MakeResult(); + auto res = std::make_unique(); res->Position.x = _loc.x + 16; res->Position.y = _loc.y + 16; res->Position.z = baseHeight; @@ -289,7 +314,7 @@ public: rct_scenery_entry* sceneryEntry = get_small_scenery_entry(_sceneryType); if (sceneryEntry == nullptr) { - return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_POSITION_THIS_HERE); + return std::make_unique(GA_ERROR::INVALID_PARAMETERS); } auto quadrant = _quadrant; @@ -383,10 +408,11 @@ public: _loc.x, _loc.y, zLow, zHigh, &map_place_scenery_clear_func, quarterTile, GetFlags() | GAME_COMMAND_FLAG_APPLY, &clearCost, CREATE_CROSSING_MODE_NONE)) { - return MakeResult(GA_ERROR::DISALLOWED, STR_CANT_POSITION_THIS_HERE, gGameCommandErrorText, gCommonFormatArgs); + return std::make_unique( + GA_ERROR::DISALLOWED, gGameCommandErrorText, gCommonFormatArgs); } - gSceneryGroundFlags = gMapGroundFlags & (ELEMENT_IS_ABOVE_GROUND | ELEMENT_IS_UNDERGROUND); + res->GroundFlags = gMapGroundFlags & (ELEMENT_IS_ABOVE_GROUND | ELEMENT_IS_UNDERGROUND); res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; res->Cost = (sceneryEntry->small_scenery.price * 10) + clearCost; diff --git a/src/openrct2/world/Scenery.cpp b/src/openrct2/world/Scenery.cpp index 022197a0f4..77eb443ad6 100644 --- a/src/openrct2/world/Scenery.cpp +++ b/src/openrct2/world/Scenery.cpp @@ -63,8 +63,6 @@ int16_t gSceneryShiftPressZOffset; int16_t gSceneryCtrlPressed; int16_t gSceneryCtrlPressZ; -uint8_t gSceneryGroundFlags; - money32 gClearSceneryCost; // rct2: 0x009A3E74 diff --git a/src/openrct2/world/Scenery.h b/src/openrct2/world/Scenery.h index 528524b672..9fd3c62581 100644 --- a/src/openrct2/world/Scenery.h +++ b/src/openrct2/world/Scenery.h @@ -279,8 +279,6 @@ extern int16_t gSceneryShiftPressZOffset; extern int16_t gSceneryCtrlPressed; extern int16_t gSceneryCtrlPressZ; -extern uint8_t gSceneryGroundFlags; - extern const LocationXY8 ScenerySubTileOffsets[]; extern money32 gClearSceneryCost;