diff --git a/src/openrct2/world/Map.cpp b/src/openrct2/world/Map.cpp index 764351cc54..b14f903e98 100644 --- a/src/openrct2/world/Map.cpp +++ b/src/openrct2/world/Map.cpp @@ -119,6 +119,8 @@ static TilePointerIndex _tileIndex; static std::vector _tileElements; static TilePointerIndex _tileIndexStash; static std::vector _tileElementsStash; +static size_t _tileElementsInUse; +static size_t _tileElementsInUseStash; static int32_t _mapSizeUnitsStash; static int32_t _mapSizeMinus2Stash; static int32_t _mapSizeStash; @@ -132,6 +134,7 @@ void StashMap() _mapSizeMinus2Stash = gMapSizeMinus2; _mapSizeStash = gMapSize; _currentRotationStash = gCurrentRotation; + _tileElementsInUseStash = _tileElementsInUse; } void UnstashMap() @@ -142,6 +145,7 @@ void UnstashMap() gMapSizeMinus2 = _mapSizeMinus2Stash; gMapSize = _mapSizeStash; gCurrentRotation = _currentRotationStash; + _tileElementsInUse = _tileElementsInUseStash; } const std::vector& GetTileElements() @@ -153,6 +157,7 @@ void SetTileElements(std::vector&& tileElements) { _tileElements = std::move(tileElements); _tileIndex = TilePointerIndex(MAXIMUM_MAP_SIZE_TECHNICAL, _tileElements.data()); + _tileElementsInUse = _tileElements.size(); } static void ReorganiseTileElements(size_t capacity) @@ -199,27 +204,39 @@ void ReorganiseTileElements() ReorganiseTileElements(_tileElements.size()); } -static bool map_check_free_elements_and_reorganise(size_t numElements) +static bool map_check_free_elements_and_reorganise(size_t numElementsOnTile, size_t numNewElements) { + // Check hard cap on num in use tiles (this would be the size of _tileElements immediately after a reorg) + if (_tileElementsInUse + numNewElements > MAX_TILE_ELEMENTS) + { + gGameCommandErrorText = STR_ERR_LANDSCAPE_DATA_AREA_FULL; + return false; + } + + auto totalElementsRequired = numElementsOnTile + numNewElements; auto freeElements = _tileElements.capacity() - _tileElements.size(); - if (freeElements >= numElements) + if (freeElements >= totalElementsRequired) { return true; } else { - auto newCapacity = std::min(MAX_TILE_ELEMENTS, _tileElements.capacity() * 2); - if (newCapacity - _tileElements.size() < numElements) + // if space issue is due to fragmentation then Reorg Tiles without increasing capacity + if (_tileElements.size() > totalElementsRequired + _tileElementsInUse) { - // Limit reached - gGameCommandErrorText = STR_ERR_LANDSCAPE_DATA_AREA_FULL; - return false; - } - else - { - ReorganiseTileElements(newCapacity); - return true; + ReorganiseTileElements(); + // This check is not expected to fail + freeElements = _tileElements.capacity() - _tileElements.size(); + if (freeElements >= totalElementsRequired) + { + return true; + } } + + // Capacity must increase to handle the space (Note capacity can go above MAX_TILE_ELEMENTS) + auto newCapacity = _tileElements.capacity() * 2; + ReorganiseTileElements(newCapacity); + return true; } } @@ -228,7 +245,7 @@ static size_t CountElementsOnTile(const CoordsXY& loc); bool MapCheckCapacityAndReorganise(const CoordsXY& loc, size_t numElements) { auto numElementsOnTile = CountElementsOnTile(loc); - return map_check_free_elements_and_reorganise(numElementsOnTile + numElements); + return map_check_free_elements_and_reorganise(numElementsOnTile, numElements); } static void clear_elements_at(const CoordsXY& loc); @@ -1011,7 +1028,7 @@ void tile_element_remove(TileElement* tileElement) // Mark the latest element with the last element flag. (tileElement - 1)->SetLastForTile(true); tileElement->base_height = MAX_ELEMENT_HEIGHT; - + _tileElementsInUse--; if (tileElement == &_tileElements.back()) { _tileElements.pop_back(); @@ -1127,16 +1144,17 @@ static size_t CountElementsOnTile(const CoordsXY& loc) return count; } -static TileElement* AllocateTileElements(size_t count) +static TileElement* AllocateTileElements(size_t numElementsOnTile, size_t numNewElements) { - if (!map_check_free_elements_and_reorganise(count)) + if (!map_check_free_elements_and_reorganise(numElementsOnTile, numNewElements)) { log_error("Cannot insert new element"); return nullptr; } auto oldSize = _tileElements.size(); - _tileElements.resize(_tileElements.size() + count); + _tileElements.resize(_tileElements.size() + numElementsOnTile + numNewElements); + _tileElementsInUse += numNewElements; return &_tileElements[oldSize]; } @@ -1149,8 +1167,7 @@ TileElement* tile_element_insert(const CoordsXYZ& loc, int32_t occupiedQuadrants const auto& tileLoc = TileCoordsXYZ(loc); auto numElementsOnTileOld = CountElementsOnTile(loc); - auto numElementsOnTileNew = numElementsOnTileOld + 1; - auto* newTileElement = AllocateTileElements(numElementsOnTileNew); + auto* newTileElement = AllocateTileElements(numElementsOnTileOld, 1); auto* originalTileElement = _tileIndex.GetFirstElementAt(tileLoc); if (newTileElement == nullptr) {