diff --git a/src/openrct2-ui/windows/Sign.cpp b/src/openrct2-ui/windows/Sign.cpp index b6f876b8c1..eea34bc9db 100644 --- a/src/openrct2-ui/windows/Sign.cpp +++ b/src/openrct2-ui/windows/Sign.cpp @@ -123,6 +123,7 @@ rct_window* window_sign_open(rct_windownumber number) if (tile_element == nullptr) return nullptr; + auto& tileElements = GetTileElements(); while (1) { if (tile_element->GetType() == TILE_ELEMENT_TYPE_LARGE_SCENERY) @@ -137,7 +138,7 @@ rct_window* window_sign_open(rct_windownumber number) } } tile_element++; - if (tile_element >= &gTileElements[std::size(gTileElements)]) + if (tile_element >= &tileElements[tileElements.size()]) { return nullptr; } @@ -180,6 +181,8 @@ static void window_sign_mouseup(rct_window* w, rct_widgetindex widgetIndex) auto tile_element = map_get_first_element_at(bannerCoords); if (tile_element == nullptr) return; + + auto& tileElements = GetTileElements(); while (1) { if (tile_element->GetType() == TILE_ELEMENT_TYPE_LARGE_SCENERY) @@ -193,7 +196,7 @@ static void window_sign_mouseup(rct_window* w, rct_widgetindex widgetIndex) } } tile_element++; - if (tile_element >= &gTileElements[std::size(gTileElements)]) + if (tile_element >= &tileElements[tileElements.size()]) { return; } diff --git a/src/openrct2/ParkFile.cpp b/src/openrct2/ParkFile.cpp index a79112950c..d06701f941 100644 --- a/src/openrct2/ParkFile.cpp +++ b/src/openrct2/ParkFile.cpp @@ -781,14 +781,22 @@ namespace OpenRCT2 if (cs.GetMode() == OrcaStream::Mode::READING) { OpenRCT2::GetContext()->GetGameState()->InitAll(gMapSize); + + auto numElements = cs.Read(); + + std::vector tileElements; + tileElements.resize(numElements); + cs.Read(tileElements.data(), tileElements.size() * sizeof(TileElement)); + SetTileElements(std::move(tileElements)); + UpdateParkEntranceLocations(); + } + else + { + ReorganiseTileElements(); + const auto& tileElements = GetTileElements(); + cs.Write(static_cast(tileElements.size())); + cs.Write(tileElements.data(), tileElements.size() * sizeof(TileElement)); } - - std::vector tiles(std::begin(gTileElements), std::end(gTileElements)); - cs.ReadWriteVector(tiles, [&cs](TileElement& el) { cs.ReadWrite(&el, sizeof(TileElement)); }); - std::copy_n(tiles.data(), std::min(tiles.size(), std::size(gTileElements)), gTileElements); - - map_update_tile_pointers(); - UpdateParkEntranceLocations(); }); if (!found) { @@ -1560,16 +1568,12 @@ namespace OpenRCT2 void ParkFileExporter::Export(std::string_view path) { - map_reorganise_elements(); - auto parkFile = std::make_unique(); parkFile->Save(path); } void ParkFileExporter::Export(IStream& stream) { - map_reorganise_elements(); - auto parkFile = std::make_unique(); parkFile->ExportObjectsList = ExportObjectsList; parkFile->Save(stream); @@ -1598,7 +1602,6 @@ int32_t scenario_save(const utf8* path, int32_t flags) window_close_construction_windows(); } - map_reorganise_elements(); viewport_set_saved_view(); bool result = false; diff --git a/src/openrct2/core/OrcaStream.hpp b/src/openrct2/core/OrcaStream.hpp index 0bebc9058e..e8ed2c584b 100644 --- a/src/openrct2/core/OrcaStream.hpp +++ b/src/openrct2/core/OrcaStream.hpp @@ -391,7 +391,7 @@ namespace OpenRCT2 { if (_mode == Mode::READING) { - T temp; + T temp{}; ReadWrite(temp); } else diff --git a/src/openrct2/interface/InteractiveConsole.cpp b/src/openrct2/interface/InteractiveConsole.cpp index 659c20f81a..024e19aa0d 100644 --- a/src/openrct2/interface/InteractiveConsole.cpp +++ b/src/openrct2/interface/InteractiveConsole.cpp @@ -1238,8 +1238,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 76d9c721b2..f8ecdadbf0 100644 --- a/src/openrct2/network/NetworkBase.cpp +++ b/src/openrct2/network/NetworkBase.cpp @@ -2756,7 +2756,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 9703aa49fe..ae9b91843d 100644 --- a/src/openrct2/rct1/S4Importer.cpp +++ b/src/openrct2/rct1/S4Importer.cpp @@ -1536,38 +1536,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 b010a499bf..dcbc079790 100644 --- a/src/openrct2/rct2/S6Importer.cpp +++ b/src/openrct2/rct2/S6Importer.cpp @@ -453,7 +453,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(); @@ -1028,7 +1027,7 @@ 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; bool nextElementInvisible = false; bool restOfTileInvisible = false; for (TileCoordsXY coords = { 0, 0 }; coords.y < MAXIMUM_MAP_SIZE_TECHNICAL; coords.y++) @@ -1040,9 +1039,9 @@ public: 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; } @@ -1050,9 +1049,9 @@ 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; } @@ -1076,19 +1075,19 @@ public: continue; } - ImportTileElement(dstElement, srcElement, nextElementInvisible || restOfTileInvisible); + auto& dstElement = tileElements.emplace_back(); + ImportTileElement(&dstElement, srcElement, nextElementInvisible || restOfTileInvisible); nextElementInvisible = false; - dstElement++; } while (!(srcElement++)->IsLastForTile()); // Set last element flag in case the original last element was never added - (dstElement - 1)->SetLastForTile(true); + 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, bool invisible) diff --git a/src/openrct2/ride/TrackDesign.cpp b/src/openrct2/ride/TrackDesign.cpp index 44292c0e0f..e8f345ef39 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) @@ -1984,12 +1980,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) @@ -2003,7 +1994,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; @@ -2086,43 +2077,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(); } /** @@ -2131,26 +2086,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 4e67d9b352..b2a50db964 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 ef72403de3..b9c95380e7 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 7a8794d635..d3da1647a4 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 b3b6fdefe3..7c902dd21d 100644 --- a/test/tests/PlayTests.cpp +++ b/test/tests/PlayTests.cpp @@ -53,7 +53,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 d82472a897..8adaf1fe33 100644 --- a/test/tests/S6ImportExportTests.cpp +++ b/test/tests/S6ImportExportTests.cpp @@ -68,7 +68,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();