From 71318dbcb35e716e99b10fcd666b1e28af5cec43 Mon Sep 17 00:00:00 2001 From: Hielke Morsink Date: Sun, 15 May 2022 00:00:47 +0200 Subject: [PATCH] Fix #17205: Crash in mapgen when base terrain is not selected --- distribution/changelog.txt | 1 + src/openrct2/world/MapGen.cpp | 42 +++++++++++++++++++++++------------ 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/distribution/changelog.txt b/distribution/changelog.txt index ad6ab81e3a..12780c1b82 100644 --- a/distribution/changelog.txt +++ b/distribution/changelog.txt @@ -19,6 +19,7 @@ - Fix: [#17099] Object selection thumbnail box is one pixel too tall. - Fix: [#17104] Changing map size does not invalidate park size. - Fix: [#17197] Segfault when extracting files from the GOG installer. +- Fix: [#17205] Map generator sometimes crashes when not all standard terrain objects are available. 0.4.0 (2022-04-25) ------------------------------------------------------------------------ diff --git a/src/openrct2/world/MapGen.cpp b/src/openrct2/world/MapGen.cpp index 3640534ce8..2f87e65520 100644 --- a/src/openrct2/world/MapGen.cpp +++ b/src/openrct2/world/MapGen.cpp @@ -144,12 +144,23 @@ void mapgen_generate(mapgen_settings* settings) auto mapSize = settings->mapSize; auto waterLevel = settings->water_level; const auto selectedFloor = TerrainSurfaceObject::GetById(settings->floor); - std::string floorTexture = selectedFloor != nullptr ? std::string(selectedFloor->GetIdentifier()) : ""; + std::string_view floorTexture = selectedFloor != nullptr ? selectedFloor->GetIdentifier() : ""; const auto selectedEdge = TerrainEdgeObject::GetById(settings->wall); - std::string edgeTexture = selectedFloor != nullptr ? std::string(selectedEdge->GetIdentifier()) : ""; + std::string_view edgeTexture = selectedFloor != nullptr ? selectedEdge->GetIdentifier() : ""; if (floorTexture.empty()) - floorTexture = BaseTerrain[util_rand() % std::size(BaseTerrain)]; + { + std::vector availableTerrains; + std::copy_if(std::begin(BaseTerrain), std::end(BaseTerrain), std::back_inserter(availableTerrains), [](auto terrain) { + return object_manager_get_loaded_object(ObjectEntryDescriptor(terrain)) != nullptr; + }); + + if (availableTerrains.empty()) + // Fall back to the first available surface texture that is available in the park + floorTexture = TerrainSurfaceObject::GetById(0)->GetIdentifier(); + else + floorTexture = availableTerrains[util_rand() % availableTerrains.size()]; + } if (edgeTexture.empty()) { @@ -160,6 +171,10 @@ void mapgen_generate(mapgen_settings* settings) edgeTexture = "rct2.terrain_edge.ice"; else edgeTexture = "rct2.terrain_edge.rock"; + + // Fall back to the first available edge texture that is available in the park + if (object_manager_get_loaded_object(ObjectEntryDescriptor(edgeTexture)) == nullptr) + edgeTexture = TerrainEdgeObject::GetById(0)->GetIdentifier(); } auto floorTextureId = object_manager_get_loaded_object_entry_index(ObjectEntryDescriptor(floorTexture)); @@ -205,18 +220,17 @@ void mapgen_generate(mapgen_settings* settings) mapgen_set_water_level(waterLevel); // Add sandy beaches - std::string beachTexture = std::string(floorTexture); - if (settings->floor == -1 && floorTexture == "rct2.terrain_surface.grass") + std::string_view beachTexture = floorTexture; + if (settings->floor == -1 && floorTexture == "rct2.terrain_surface.grass" && (util_rand() & 1)) { - switch (util_rand() % 4) - { - case 0: - beachTexture = "rct2.terrain_surface.sand"; - break; - case 1: - beachTexture = "rct2.terrain_surface.sand_brown"; - break; - } + std::vector availableBeachTextures; + if (object_manager_get_loaded_object(ObjectEntryDescriptor("rct2.terrain_surface.sand")) != nullptr) + availableBeachTextures.push_back("rct2.terrain_surface.sand"); + if (object_manager_get_loaded_object(ObjectEntryDescriptor("rct2.terrain_surface.sand_brown")) != nullptr) + availableBeachTextures.push_back("rct2.terrain_surface.sand_brown"); + + if (!availableBeachTextures.empty()) + beachTexture = availableBeachTextures[util_rand() % availableBeachTextures.size()]; } auto beachTextureId = object_manager_get_loaded_object_entry_index(ObjectEntryDescriptor(beachTexture));