From bbef85e7526bf71a08d896d5131364733e5a3ea8 Mon Sep 17 00:00:00 2001 From: Duncan Date: Wed, 21 Jul 2021 13:01:09 +0100 Subject: [PATCH] Fix #15028, #15042. Crash when placing large scenery (#15043) * Fix #15028, #15042. Crash when placing large scenery When the fragmented tile element limit is reached whilst placing a large scenery ghost the game will perform a reorg of the map elements invalidating tile element pointers. As large scenery are multi tiled this can happen mid action invalidating the pointer that was allocated for the first tile element. Large scenery actions pass back the first tile element pointer to the calling function for use with ghost removal. When this pointer is invalid it causes the crash. The ultimate fix for this would be to create an undo function for actions. As an interim the function has been modified to return the first tile height. * Update changelog --- distribution/changelog.txt | 1 + src/openrct2-ui/windows/TopToolbar.cpp | 4 +--- src/openrct2/actions/LargeSceneryPlaceAction.cpp | 4 ++-- src/openrct2/actions/LargeSceneryPlaceAction.h | 2 +- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/distribution/changelog.txt b/distribution/changelog.txt index ebb91fefa9..c1e5ddf9ff 100644 --- a/distribution/changelog.txt +++ b/distribution/changelog.txt @@ -1,5 +1,6 @@ 0.3.4+ (in development) ------------------------------------------------------------------------ +- Fix: [#15028] Crash when placing large scenery. - Improved: [#12626] Allow using RCT2 saves to mark RCT Classic (.sea) parks as finished and vice versa. 0.3.4 (2021-07-19) diff --git a/src/openrct2-ui/windows/TopToolbar.cpp b/src/openrct2-ui/windows/TopToolbar.cpp index 05c56dd72c..5c0d53c84b 100644 --- a/src/openrct2-ui/windows/TopToolbar.cpp +++ b/src/openrct2-ui/windows/TopToolbar.cpp @@ -2549,9 +2549,7 @@ static money32 try_place_ghost_large_scenery( gSceneryPlaceRotation = loc.direction; - TileElement* tileElement = lspar->tileElement; - gSceneryGhostPosition = { loc, tileElement->GetBaseZ() }; - + gSceneryGhostPosition = { loc, lspar->firstTileHeight }; if (lspar->GroundFlags & ELEMENT_IS_UNDERGROUND) { // Set underground on diff --git a/src/openrct2/actions/LargeSceneryPlaceAction.cpp b/src/openrct2/actions/LargeSceneryPlaceAction.cpp index ccc6a13ed5..84d7fb8de2 100644 --- a/src/openrct2/actions/LargeSceneryPlaceAction.cpp +++ b/src/openrct2/actions/LargeSceneryPlaceAction.cpp @@ -284,12 +284,12 @@ GameActions::Result::Ptr LargeSceneryPlaceAction::Execute() const SetNewLargeSceneryElement(*newSceneryElement, tileNum); map_animation_create(MAP_ANIMATION_TYPE_LARGE_SCENERY, { curTile, zLow }); + map_invalidate_tile_full(curTile); if (tileNum == 0) { - res->tileElement = newSceneryElement->as(); + res->firstTileHeight = zLow; } - map_invalidate_tile_full(curTile); } // Allocate banner after all tiles to ensure banner id doesn't need to be freed. diff --git a/src/openrct2/actions/LargeSceneryPlaceAction.h b/src/openrct2/actions/LargeSceneryPlaceAction.h index e96ea00278..85b95cf197 100644 --- a/src/openrct2/actions/LargeSceneryPlaceAction.h +++ b/src/openrct2/actions/LargeSceneryPlaceAction.h @@ -22,7 +22,7 @@ public: LargeSceneryPlaceActionResult(GameActions::Status error, rct_string_id message, uint8_t* args); uint8_t GroundFlags{ 0 }; - TileElement* tileElement = nullptr; + int32_t firstTileHeight{ 0 }; }; DEFINE_GAME_ACTION(LargeSceneryPlaceAction, GameCommand::PlaceLargeScenery, LargeSceneryPlaceActionResult)