From 81d7c3f02b6370520c5c41d2dee878326fc2341f Mon Sep 17 00:00:00 2001 From: Ted John Date: Fri, 9 Apr 2021 02:09:08 +0100 Subject: [PATCH] Make tile elements dynamic and resizeable --- src/openrct2/interface/InteractiveConsole.cpp | 4 +- src/openrct2/network/NetworkBase.cpp | 1 - src/openrct2/rct1/S4Importer.cpp | 44 +-- src/openrct2/rct2/S6Importer.cpp | 35 +-- src/openrct2/ride/TrackDesign.cpp | 83 ++--- src/openrct2/world/Map.cpp | 284 ++++++++++-------- src/openrct2/world/Map.h | 25 +- src/openrct2/world/MapGen.cpp | 2 - test/tests/PlayTests.cpp | 1 - test/tests/S6ImportExportTests.cpp | 1 - 10 files changed, 235 insertions(+), 245 deletions(-) diff --git a/src/openrct2/interface/InteractiveConsole.cpp b/src/openrct2/interface/InteractiveConsole.cpp index be76cf1240..d4bcb31c38 100644 --- a/src/openrct2/interface/InteractiveConsole.cpp +++ b/src/openrct2/interface/InteractiveConsole.cpp @@ -1247,8 +1247,8 @@ static int32_t cc_remove_park_fences(InteractiveConsole& console, [[maybe_unused static int32_t cc_show_limits(InteractiveConsole& console, [[maybe_unused]] const arguments_t& argv) { - map_reorganise_elements(); - int32_t tileElementCount = gNextFreeTileElement - gTileElements - 1; + const auto& tileElements = GetTileElements(); + int32_t tileElementCount = tileElements.size(); int32_t rideCount = ride_get_count(); int32_t spriteCount = 0; diff --git a/src/openrct2/network/NetworkBase.cpp b/src/openrct2/network/NetworkBase.cpp index 5a500559e5..a4ee899bba 100644 --- a/src/openrct2/network/NetworkBase.cpp +++ b/src/openrct2/network/NetworkBase.cpp @@ -2770,7 +2770,6 @@ bool NetworkBase::LoadMap(IStream* stream) bool NetworkBase::SaveMap(IStream* stream, const std::vector& objects) const { bool result = false; - map_reorganise_elements(); viewport_set_saved_view(); try { diff --git a/src/openrct2/rct1/S4Importer.cpp b/src/openrct2/rct1/S4Importer.cpp index 11dab91d4e..2ba5a6db61 100644 --- a/src/openrct2/rct1/S4Importer.cpp +++ b/src/openrct2/rct1/S4Importer.cpp @@ -1483,38 +1483,44 @@ private: // Build tile pointer cache (needed to get the first element at a certain location) auto tilePointerIndex = TilePointerIndex(RCT1_MAX_MAP_SIZE, _s4.tile_elements); - TileElement* dstElement = gTileElements; - + std::vector tileElements; for (TileCoordsXY coords = { 0, 0 }; coords.y < MAXIMUM_MAP_SIZE_TECHNICAL; coords.y++) { for (coords.x = 0; coords.x < MAXIMUM_MAP_SIZE_TECHNICAL; coords.x++) { if (coords.x >= RCT1_MAX_MAP_SIZE || coords.y >= RCT1_MAX_MAP_SIZE) { - dstElement->ClearAs(TILE_ELEMENT_TYPE_SURFACE); - dstElement->SetLastForTile(true); - dstElement++; - continue; + auto& dstElement = tileElements.emplace_back(); + dstElement.ClearAs(TILE_ELEMENT_TYPE_SURFACE); + dstElement.SetLastForTile(true); } - - // This is the equivalent of map_get_first_element_at(x, y), but on S4 data. - RCT12TileElement* srcElement = tilePointerIndex.GetFirstElementAt(coords); - do + else { - if (srcElement->base_height == RCT12_MAX_ELEMENT_HEIGHT) - continue; + // This is the equivalent of map_get_first_element_at(x, y), but on S4 data. + RCT12TileElement* srcElement = tilePointerIndex.GetFirstElementAt(coords); + do + { + if (srcElement->base_height == RCT12_MAX_ELEMENT_HEIGHT) + continue; - auto numAddedElements = ImportTileElement(dstElement, srcElement); - dstElement += numAddedElements; - } while (!(srcElement++)->IsLastForTile()); + // Reserve 8 elements for import + auto originalSize = tileElements.size(); + tileElements.resize(originalSize + 16); + auto dstElement = tileElements.data() + originalSize; + auto numAddedElements = ImportTileElement(dstElement, srcElement); + tileElements.resize(originalSize + numAddedElements); + } while (!(srcElement++)->IsLastForTile()); - // Set last element flag in case the original last element was never added - (dstElement - 1)->SetLastForTile(true); + // Set last element flag in case the original last element was never added + if (tileElements.size() > 0) + { + tileElements.back().SetLastForTile(true); + } + } } } - map_update_tile_pointers(); - + SetTileElements(std::move(tileElements)); FixEntrancePositions(); } diff --git a/src/openrct2/rct2/S6Importer.cpp b/src/openrct2/rct2/S6Importer.cpp index e1d075c976..b53146b107 100644 --- a/src/openrct2/rct2/S6Importer.cpp +++ b/src/openrct2/rct2/S6Importer.cpp @@ -465,7 +465,6 @@ public: // Fix and set dynamic variables map_strip_ghost_flag_from_elements(); - map_update_tile_pointers(); game_convert_strings_to_utf8(); map_count_remaining_land_rights(); determine_ride_entrance_and_exit_locations(); @@ -1040,16 +1039,16 @@ public: // Build tile pointer cache (needed to get the first element at a certain location) auto tilePointerIndex = TilePointerIndex(RCT2_MAXIMUM_MAP_SIZE_TECHNICAL, _s6.tile_elements); - TileElement* dstElement = gTileElements; + std::vector tileElements; for (TileCoordsXY coords = { 0, 0 }; coords.y < MAXIMUM_MAP_SIZE_TECHNICAL; coords.y++) { for (coords.x = 0; coords.x < MAXIMUM_MAP_SIZE_TECHNICAL; coords.x++) { if (coords.x >= RCT2_MAXIMUM_MAP_SIZE_TECHNICAL || coords.y >= RCT2_MAXIMUM_MAP_SIZE_TECHNICAL) { - dstElement->ClearAs(TILE_ELEMENT_TYPE_SURFACE); - dstElement->SetLastForTile(true); - dstElement++; + auto& dstElement = tileElements.emplace_back(); + dstElement.ClearAs(TILE_ELEMENT_TYPE_SURFACE); + dstElement.SetLastForTile(true); continue; } @@ -1057,17 +1056,18 @@ public: // This might happen with damaged parks. Make sure there is *something* to avoid crashes. if (srcElement == nullptr) { - dstElement->ClearAs(TILE_ELEMENT_TYPE_SURFACE); - dstElement->SetLastForTile(true); - dstElement++; + auto& dstElement = tileElements.emplace_back(); + dstElement.ClearAs(TILE_ELEMENT_TYPE_SURFACE); + dstElement.SetLastForTile(true); continue; } do { + auto& dstElement = tileElements.emplace_back(); if (srcElement->base_height == RCT12_MAX_ELEMENT_HEIGHT) { - std::memcpy(dstElement, srcElement, sizeof(*srcElement)); + std::memcpy(&dstElement, srcElement, sizeof(*srcElement)); } else { @@ -1076,19 +1076,20 @@ public: if (tileElementType == RCT12TileElementType::Corrupt || tileElementType == RCT12TileElementType::EightCarsCorrupt14 || tileElementType == RCT12TileElementType::EightCarsCorrupt15) - std::memcpy(dstElement, srcElement, sizeof(*srcElement)); + std::memcpy(&dstElement, srcElement, sizeof(*srcElement)); else - ImportTileElement(dstElement, srcElement); + ImportTileElement(&dstElement, srcElement); } - - dstElement++; } while (!(srcElement++)->IsLastForTile()); + + // Set last element flag in case the original last element was never added + if (tileElements.size() > 0) + { + tileElements.back().SetLastForTile(true); + } } } - - gNextFreeTileElementPointerIndex = _s6.next_free_tile_element_pointer_index; - - map_update_tile_pointers(); + SetTileElements(std::move(tileElements)); } void ImportTileElement(TileElement* dst, const RCT12TileElement* src) diff --git a/src/openrct2/ride/TrackDesign.cpp b/src/openrct2/ride/TrackDesign.cpp index f63e7c30a9..496b2c1e45 100644 --- a/src/openrct2/ride/TrackDesign.cpp +++ b/src/openrct2/ride/TrackDesign.cpp @@ -96,10 +96,6 @@ static bool _trackDesignPlaceStateHasScenery = false; static bool _trackDesignPlaceStatePlaceScenery = true; static bool _trackDesignPlaceIsReplay = false; -static std::unique_ptr track_design_preview_backup_map(); - -static void track_design_preview_restore_map(map_backup* backup); - static void track_design_preview_clear_map(); rct_string_id TrackDesign::CreateTrackDesign(const Ride& ride) @@ -1985,12 +1981,7 @@ static bool track_design_place_preview(TrackDesign* td6, money32* cost, Ride** o */ void track_design_draw_preview(TrackDesign* td6, uint8_t* pixels) { - // Make a copy of the map - auto mapBackup = track_design_preview_backup_map(); - if (mapBackup == nullptr) - { - return; - } + StashMap(); track_design_preview_clear_map(); if (gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER) @@ -2004,7 +1995,7 @@ void track_design_draw_preview(TrackDesign* td6, uint8_t* pixels) if (!track_design_place_preview(td6, &cost, &ride, &flags)) { std::fill_n(pixels, TRACK_PREVIEW_IMAGE_SIZE * 4, 0x00); - track_design_preview_restore_map(mapBackup.get()); + UnstashMap(); return; } td6->cost = cost; @@ -2087,43 +2078,7 @@ void track_design_draw_preview(TrackDesign* td6, uint8_t* pixels) } ride->Delete(); - track_design_preview_restore_map(mapBackup.get()); -} - -/** - * Create a backup of the map as it will be cleared for drawing the track - * design preview. - * rct2: 0x006D1C68 - */ -static std::unique_ptr track_design_preview_backup_map() -{ - auto backup = std::make_unique(); - if (backup != nullptr) - { - std::memcpy(backup->tile_elements, gTileElements, sizeof(backup->tile_elements)); - std::memcpy(backup->tile_pointers, gTileElementTilePointers, sizeof(backup->tile_pointers)); - backup->next_free_tile_element = gNextFreeTileElement; - backup->map_size_units = gMapSizeUnits; - backup->map_size_units_minus_2 = gMapSizeMinus2; - backup->map_size = gMapSize; - backup->current_rotation = get_current_rotation(); - } - return backup; -} - -/** - * Restores the map from a backup. - * rct2: 0x006D2378 - */ -static void track_design_preview_restore_map(map_backup* backup) -{ - std::memcpy(gTileElements, backup->tile_elements, sizeof(backup->tile_elements)); - std::memcpy(gTileElementTilePointers, backup->tile_pointers, sizeof(backup->tile_pointers)); - gNextFreeTileElement = backup->next_free_tile_element; - gMapSizeUnits = backup->map_size_units; - gMapSizeMinus2 = backup->map_size_units_minus_2; - gMapSize = backup->map_size; - gCurrentRotation = backup->current_rotation; + UnstashMap(); } /** @@ -2132,26 +2087,30 @@ static void track_design_preview_restore_map(map_backup* backup) */ static void track_design_preview_clear_map() { - // These values were previously allocated in backup map but - // it seems more fitting to place in this function + auto numTiles = MAXIMUM_MAP_SIZE_TECHNICAL * MAXIMUM_MAP_SIZE_TECHNICAL; + gMapSizeUnits = 255 * COORDS_XY_STEP; gMapSizeMinus2 = (264 * 32) - 2; gMapSize = 256; - for (int32_t i = 0; i < MAX_TILE_TILE_ELEMENT_POINTERS; i++) + // Reserve ~8 elements per tile + std::vector tileElements; + tileElements.reserve(numTiles * 8); + + for (int32_t i = 0; i < numTiles; i++) { - TileElement* tile_element = &gTileElements[i]; - tile_element->ClearAs(TILE_ELEMENT_TYPE_SURFACE); - tile_element->SetLastForTile(true); - tile_element->AsSurface()->SetSlope(TILE_ELEMENT_SLOPE_FLAT); - tile_element->AsSurface()->SetWaterHeight(0); - tile_element->AsSurface()->SetSurfaceStyle(0); - tile_element->AsSurface()->SetEdgeStyle(0); - tile_element->AsSurface()->SetGrassLength(GRASS_LENGTH_CLEAR_0); - tile_element->AsSurface()->SetOwnership(OWNERSHIP_OWNED); - tile_element->AsSurface()->SetParkFences(0); + auto* element = &tileElements.emplace_back(); + element->ClearAs(TILE_ELEMENT_TYPE_SURFACE); + element->SetLastForTile(true); + element->AsSurface()->SetSlope(TILE_ELEMENT_SLOPE_FLAT); + element->AsSurface()->SetWaterHeight(0); + element->AsSurface()->SetSurfaceStyle(0); + element->AsSurface()->SetEdgeStyle(0); + element->AsSurface()->SetGrassLength(GRASS_LENGTH_CLEAR_0); + element->AsSurface()->SetOwnership(OWNERSHIP_OWNED); + element->AsSurface()->SetParkFences(0); } - map_update_tile_pointers(); + SetTileElements(std::move(tileElements)); } bool track_design_are_entrance_and_exit_placed() diff --git a/src/openrct2/world/Map.cpp b/src/openrct2/world/Map.cpp index 1d70a4e1e8..2979ebd270 100644 --- a/src/openrct2/world/Map.cpp +++ b/src/openrct2/world/Map.cpp @@ -81,6 +81,8 @@ const std::array CoordsDirectionDelta = { const TileCoordsXY TileDirectionDelta[] = { { -1, 0 }, { 0, +1 }, { +1, 0 }, { 0, -1 }, { -1, +1 }, { +1, +1 }, { +1, -1 }, { -1, -1 } }; +constexpr size_t MIN_TILE_ELEMENTS = 1024; + uint16_t gMapSelectFlags; uint16_t gMapSelectType; CoordsXY gMapSelectPositionA; @@ -99,14 +101,9 @@ int16_t gMapSize; int16_t gMapSizeMaxXY; int16_t gMapBaseZ; -TileElement gTileElements[MAX_TILE_ELEMENTS_WITH_SPARE_ROOM]; -TileElement* gTileElementTilePointers[MAX_TILE_TILE_ELEMENT_POINTERS]; std::vector gMapSelectionTiles; std::vector gPeepSpawns; -TileElement* gNextFreeTileElement; -uint32_t gNextFreeTileElementPointerIndex; - bool gLandMountainMode; bool gLandPaintMode; bool gClearSmallScenery; @@ -118,6 +115,114 @@ uint16_t gLandRemainingConstructionSales; bool gMapLandRightsUpdateSuccess; +static TilePointerIndex _tileIndex; +static std::vector _tileElements; +static TilePointerIndex _tileIndexStash; +static std::vector _tileElementsStash; +static int32_t _mapSizeUnitsStash; +static int32_t _mapSizeMinus2Stash; +static int32_t _mapSizeStash; +static int32_t _currentRotationStash; + +void StashMap() +{ + _tileIndexStash = std::move(_tileIndex); + _tileElementsStash = std::move(_tileElements); + _mapSizeUnitsStash = gMapSizeUnits; + _mapSizeMinus2Stash = gMapSizeMinus2; + _mapSizeStash = gMapSize; + _currentRotationStash = gCurrentRotation; +} + +void UnstashMap() +{ + _tileIndex = std::move(_tileIndexStash); + _tileElements = std::move(_tileElementsStash); + gMapSizeUnits = _mapSizeUnitsStash; + gMapSizeMinus2 = _mapSizeMinus2Stash; + gMapSize = _mapSizeStash; + gCurrentRotation = _currentRotationStash; +} + +const std::vector& GetTileElements() +{ + return _tileElements; +} + +void SetTileElements(std::vector&& tileElements) +{ + _tileElements = std::move(tileElements); + _tileIndex = TilePointerIndex(MAXIMUM_MAP_SIZE_TECHNICAL, _tileElements.data()); +} + +static void ReorganiseTileElements(size_t capacity) +{ + context_setcurrentcursor(CursorID::ZZZ); + + std::vector newElements; + newElements.reserve(std::max(MIN_TILE_ELEMENTS, capacity)); + for (int32_t y = 0; y < MAXIMUM_MAP_SIZE_TECHNICAL; y++) + { + for (int32_t x = 0; x < MAXIMUM_MAP_SIZE_TECHNICAL; x++) + { + const auto* element = map_get_first_element_at(TileCoordsXY{ x, y }.ToCoordsXY()); + if (element == nullptr) + { + auto& newElement = newElements.emplace_back(); + newElement.ClearAs(TILE_ELEMENT_TYPE_SURFACE); + newElement.SetLastForTile(true); + newElement.base_height = 14; + newElement.clearance_height = 14; + newElement.AsSurface()->SetWaterHeight(0); + newElement.AsSurface()->SetSlope(TILE_ELEMENT_SLOPE_FLAT); + newElement.AsSurface()->SetGrassLength(GRASS_LENGTH_CLEAR_0); + newElement.AsSurface()->SetOwnership(OWNERSHIP_UNOWNED); + newElement.AsSurface()->SetParkFences(0); + newElement.AsSurface()->SetSurfaceStyle(0); + newElement.AsSurface()->SetEdgeStyle(0); + } + else + { + do + { + newElements.push_back(*element); + } while (!(element++)->IsLastForTile()); + } + } + } + + SetTileElements(std::move(newElements)); +} + +void ReorganiseTileElements() +{ + ReorganiseTileElements(_tileElements.size()); +} + +bool map_check_free_elements_and_reorganise(size_t numElements) +{ + auto freeElements = _tileElements.capacity() - _tileElements.size(); + if (freeElements >= numElements) + { + return true; + } + else + { + auto newCapacity = std::min(MAX_TILE_ELEMENTS, _tileElements.capacity() * 2); + if (newCapacity - _tileElements.size() < numElements) + { + // Limit reached + gGameCommandErrorText = STR_ERR_LANDSCAPE_DATA_AREA_FULL; + return false; + } + else + { + ReorganiseTileElements(newCapacity); + return true; + } + } +} + static void clear_elements_at(const CoordsXY& loc); static ScreenCoordsXY translate_3d_to_2d(int32_t rotation, const CoordsXY& pos); @@ -173,7 +278,7 @@ TileElement* map_get_first_element_at(const CoordsXY& elementPos) return nullptr; } auto tileElementPos = TileCoordsXY{ elementPos }; - return gTileElementTilePointers[tileElementPos.x + tileElementPos.y * MAXIMUM_MAP_SIZE_TECHNICAL]; + return _tileIndex.GetFirstElementAt(tileElementPos); } TileElement* map_get_nth_element_at(const CoordsXY& coords, int32_t n) @@ -210,7 +315,7 @@ void map_set_tile_element(const TileCoordsXY& tilePos, TileElement* elements) log_error("Trying to access element outside of range"); return; } - gTileElementTilePointers[tilePos.x + tilePos.y * MAXIMUM_MAP_SIZE_TECHNICAL] = elements; + _tileIndex.SetTile(tilePos, elements); } SurfaceElement* map_get_surface_element_at(const CoordsXY& coords) @@ -253,23 +358,26 @@ BannerElement* map_get_banner_element_at(const CoordsXYZ& bannerPos, uint8_t pos */ void map_init(int32_t size) { - gNextFreeTileElementPointerIndex = 0; + auto numTiles = MAXIMUM_MAP_SIZE_TECHNICAL * MAXIMUM_MAP_SIZE_TECHNICAL; - for (int32_t i = 0; i < MAX_TILE_TILE_ELEMENT_POINTERS; i++) + std::vector tileElements; + tileElements.resize(numTiles); + for (int32_t i = 0; i < numTiles; i++) { - TileElement* tile_element = &gTileElements[i]; - tile_element->ClearAs(TILE_ELEMENT_TYPE_SURFACE); - tile_element->SetLastForTile(true); - tile_element->base_height = 14; - tile_element->clearance_height = 14; - tile_element->AsSurface()->SetWaterHeight(0); - tile_element->AsSurface()->SetSlope(TILE_ELEMENT_SLOPE_FLAT); - tile_element->AsSurface()->SetGrassLength(GRASS_LENGTH_CLEAR_0); - tile_element->AsSurface()->SetOwnership(OWNERSHIP_UNOWNED); - tile_element->AsSurface()->SetParkFences(0); - tile_element->AsSurface()->SetSurfaceStyle(0); - tile_element->AsSurface()->SetEdgeStyle(0); + auto* element = &tileElements[i]; + element->ClearAs(TILE_ELEMENT_TYPE_SURFACE); + element->SetLastForTile(true); + element->base_height = 14; + element->clearance_height = 14; + element->AsSurface()->SetWaterHeight(0); + element->AsSurface()->SetSlope(TILE_ELEMENT_SLOPE_FLAT); + element->AsSurface()->SetGrassLength(GRASS_LENGTH_CLEAR_0); + element->AsSurface()->SetOwnership(OWNERSHIP_UNOWNED); + element->AsSurface()->SetParkFences(0); + element->AsSurface()->SetSurfaceStyle(0); + element->AsSurface()->SetEdgeStyle(0); } + SetTileElements(std::move(tileElements)); gGrassSceneryTileLoopPosition = 0; gWidePathTileLoopPosition = {}; @@ -278,7 +386,6 @@ void map_init(int32_t size) gMapSize = size; gMapSizeMaxXY = size * 32 - 33; gMapBaseZ = 7; - map_update_tile_pointers(); map_remove_out_of_range_elements(); AutoCreateMapAnimations(); @@ -338,40 +445,12 @@ void map_count_remaining_land_rights() */ void map_strip_ghost_flag_from_elements() { - for (auto& element : gTileElements) + for (auto& element : _tileElements) { element.SetGhost(false); } } -/** - * - * rct2: 0x0068AFFD - */ -void map_update_tile_pointers() -{ - int32_t i, x, y; - - for (i = 0; i < MAX_TILE_TILE_ELEMENT_POINTERS; i++) - { - gTileElementTilePointers[i] = TILE_UNDEFINED_TILE_ELEMENT; - } - - TileElement* tileElement = gTileElements; - TileElement** tile = gTileElementTilePointers; - for (y = 0; y < MAXIMUM_MAP_SIZE_TECHNICAL; y++) - { - for (x = 0; x < MAXIMUM_MAP_SIZE_TECHNICAL; x++) - { - *tile++ = tileElement; - while (!(tileElement++)->IsLastForTile()) - ; - } - } - - gNextFreeTileElement = tileElement; -} - /** * Return the absolute height of an element, given its (x,y) coordinates * @@ -925,9 +1004,9 @@ void tile_element_remove(TileElement* tileElement) (tileElement - 1)->SetLastForTile(true); tileElement->base_height = MAX_ELEMENT_HEIGHT; - if ((tileElement + 1) == gNextFreeTileElement) + if ((tileElement + 1) == &_tileElements[_tileElements.size()]) { - gNextFreeTileElement--; + _tileElements.pop_back(); } } @@ -1029,77 +1108,28 @@ void map_invalidate_selection_rect() viewports_invalidate(left, top, right, bottom); } -/** - * - * rct2: 0x0068B111 - */ -void map_reorganise_elements() +static size_t CountElementsOnTile(const CoordsXY& loc) { - context_setcurrentcursor(CursorID::ZZZ); - - auto newTileElements = std::make_unique(MAX_TILE_ELEMENTS_WITH_SPARE_ROOM); - TileElement* newElementsPtr = newTileElements.get(); - - if (newTileElements == nullptr) + size_t count = 0; + auto* element = _tileIndex.GetFirstElementAt(TileCoordsXY(loc)); + do { - log_fatal("Unable to allocate memory for map elements."); - return; - } - - for (int32_t y = 0; y < MAXIMUM_MAP_SIZE_TECHNICAL; y++) - { - for (int32_t x = 0; x < MAXIMUM_MAP_SIZE_TECHNICAL; x++) - { - TileElement* startElement = map_get_first_element_at(TileCoordsXY{ x, y }.ToCoordsXY()); - if (startElement == nullptr) - continue; - TileElement* endElement = startElement; - while (!(endElement++)->IsLastForTile()) - ; - - const auto numElements = static_cast(endElement - startElement); - std::memcpy(newElementsPtr, startElement, numElements * sizeof(TileElement)); - newElementsPtr += numElements; - } - } - - const auto numElements = static_cast(newElementsPtr - newTileElements.get()); - std::memcpy(gTileElements, newTileElements.get(), numElements * sizeof(TileElement)); - std::memset(gTileElements + numElements, 0, (MAX_TILE_ELEMENTS_WITH_SPARE_ROOM - numElements) * sizeof(TileElement)); - - map_update_tile_pointers(); + count++; + } while (!(element++)->IsLastForTile()); + return count; } -/** - * - * rct2: 0x0068B044 - * Returns true on space available for more elements - * Reorganises the map elements to check for space - */ -bool map_check_free_elements_and_reorganise(int32_t numElements) +static TileElement* AllocateTileElements(size_t count) { - if (numElements != 0) + if (!map_check_free_elements_and_reorganise(count)) { - auto tileElementEnd = &gTileElements[MAX_TILE_ELEMENTS]; - - // Check if is there is room for the required number of elements - auto newTileElementEnd = gNextFreeTileElement + numElements; - if (newTileElementEnd > tileElementEnd) - { - // Defragment the map element list - map_reorganise_elements(); - - // Check if there is any room again - newTileElementEnd = gNextFreeTileElement + numElements; - if (newTileElementEnd > tileElementEnd) - { - // Not enough spare elements left :'( - gGameCommandErrorText = STR_ERR_LANDSCAPE_DATA_AREA_FULL; - return false; - } - } + log_error("Cannot insert new element"); + return nullptr; } - return true; + + auto oldSize = _tileElements.size(); + _tileElements.resize(_tileElements.size() + count); + return &_tileElements[oldSize]; } /** @@ -1109,21 +1139,16 @@ bool map_check_free_elements_and_reorganise(int32_t numElements) TileElement* tile_element_insert(const CoordsXYZ& loc, int32_t occupiedQuadrants, TileElementType type) { const auto& tileLoc = TileCoordsXYZ(loc); - TileElement *originalTileElement, *newTileElement, *insertedElement; - bool isLastForTile = false; - if (!map_check_free_elements_and_reorganise(1)) - { - log_error("Cannot insert new element"); - return nullptr; - } - - newTileElement = gNextFreeTileElement; - originalTileElement = gTileElementTilePointers[tileLoc.y * MAXIMUM_MAP_SIZE_TECHNICAL + tileLoc.x]; + auto numElementsOnTileOld = CountElementsOnTile(loc); + auto numElementsOnTileNew = numElementsOnTileOld + 1; + auto* newTileElement = AllocateTileElements(numElementsOnTileNew); + auto* originalTileElement = _tileIndex.GetFirstElementAt(tileLoc); // Set tile index pointer to point to new element block - gTileElementTilePointers[tileLoc.y * MAXIMUM_MAP_SIZE_TECHNICAL + tileLoc.x] = newTileElement; + _tileIndex.SetTile(tileLoc, newTileElement); + bool isLastForTile = false; if (originalTileElement == nullptr) { isLastForTile = true; @@ -1150,7 +1175,7 @@ TileElement* tile_element_insert(const CoordsXYZ& loc, int32_t occupiedQuadrants } // Insert new map element - insertedElement = newTileElement; + auto* insertedElement = newTileElement; newTileElement->type = 0; newTileElement->SetType(static_cast(type)); newTileElement->SetBaseZ(loc.z); @@ -1176,7 +1201,6 @@ TileElement* tile_element_insert(const CoordsXYZ& loc, int32_t occupiedQuadrants } while (!((newTileElement - 1)->IsLastForTile())); } - gNextFreeTileElement = newTileElement; return insertedElement; } diff --git a/src/openrct2/world/Map.h b/src/openrct2/world/Map.h index a9dddc5495..707453b588 100644 --- a/src/openrct2/world/Map.h +++ b/src/openrct2/world/Map.h @@ -116,15 +116,9 @@ extern uint8_t gMapSelectArrowDirection; extern uint8_t gMapGroundFlags; -extern TileElement gTileElements[MAX_TILE_ELEMENTS_WITH_SPARE_ROOM]; -extern TileElement* gTileElementTilePointers[MAX_TILE_TILE_ELEMENT_POINTERS]; - extern std::vector gMapSelectionTiles; extern std::vector gPeepSpawns; -extern TileElement* gNextFreeTileElement; -extern uint32_t gNextFreeTileElementPointerIndex; - // Used in the land tool window to enable mountain tool / land smoothing extern bool gLandMountainMode; // Used in the land tool window to allow dragging and changing land styles @@ -146,9 +140,11 @@ extern const uint8_t tile_element_raise_styles[9][32]; template class TilePointerIndex { std::vector TilePointers; - uint16_t MapSize; + uint16_t MapSize{}; public: + TilePointerIndex() = default; + explicit TilePointerIndex(const uint16_t mapSize, T* tileElements) { MapSize = mapSize; @@ -171,13 +167,23 @@ public: { return TilePointers[coords.x + (coords.y * MapSize)]; } + + void SetTile(TileCoordsXY coords, T* tileElement) + { + TilePointers[coords.x + (coords.y * MapSize)] = tileElement; + } }; +void ReorganiseTileElements(); +const std::vector& GetTileElements(); +void SetTileElements(std::vector&& tileElements); +void StashMap(); +void UnstashMap(); + void map_init(int32_t size); void map_count_remaining_land_rights(); void map_strip_ghost_flag_from_elements(); -void map_update_tile_pointers(); TileElement* map_get_first_element_at(const CoordsXY& elementPos); TileElement* map_get_nth_element_at(const CoordsXY& coords, int32_t n); void map_set_tile_element(const TileCoordsXY& tilePos, TileElement* elements); @@ -210,8 +216,7 @@ void tile_element_remove(TileElement* tileElement); void map_remove_all_rides(); void map_invalidate_map_selection_tiles(); void map_invalidate_selection_rect(); -void map_reorganise_elements(); -bool map_check_free_elements_and_reorganise(int32_t num_elements); +bool map_check_free_elements_and_reorganise(size_t num_elements); TileElement* tile_element_insert(const CoordsXYZ& loc, int32_t occupiedQuadrants, TileElementType type); template T* TileElementInsert(const CoordsXYZ& loc, int32_t occupiedQuadrants) diff --git a/src/openrct2/world/MapGen.cpp b/src/openrct2/world/MapGen.cpp index d6753302c8..cefb0081e6 100644 --- a/src/openrct2/world/MapGen.cpp +++ b/src/openrct2/world/MapGen.cpp @@ -231,8 +231,6 @@ void mapgen_generate(mapgen_settings* settings) // Place the trees if (settings->trees != 0) mapgen_place_trees(); - - map_reorganise_elements(); } static void mapgen_place_tree(int32_t type, const CoordsXY& loc) diff --git a/test/tests/PlayTests.cpp b/test/tests/PlayTests.cpp index 3b41fb0bfb..da1ca52753 100644 --- a/test/tests/PlayTests.cpp +++ b/test/tests/PlayTests.cpp @@ -54,7 +54,6 @@ static std::unique_ptr localStartGame(const std::string& parkPath) reset_all_sprite_quadrant_placements(); scenery_set_default_placement_configuration(); load_palette(); - map_reorganise_elements(); EntityTweener::Get().Reset(); AutoCreateMapAnimations(); fix_invalid_vehicle_sprite_sizes(); diff --git a/test/tests/S6ImportExportTests.cpp b/test/tests/S6ImportExportTests.cpp index 7044adfa16..3f27396092 100644 --- a/test/tests/S6ImportExportTests.cpp +++ b/test/tests/S6ImportExportTests.cpp @@ -75,7 +75,6 @@ static void GameInit(bool retainSpatialIndices) reset_all_sprite_quadrant_placements(); scenery_set_default_placement_configuration(); load_palette(); - map_reorganise_elements(); EntityTweener::Get().Reset(); AutoCreateMapAnimations(); fix_invalid_vehicle_sprite_sizes();