mirror of
https://github.com/OpenRCT2/OpenRCT2
synced 2026-01-15 11:03:00 +01:00
Merge pull request #22735 from AaronVanGeffen/mapgen
Rework map generation window
This commit is contained in:
@@ -1910,8 +1910,8 @@ STR_2670 :Scroll
|
||||
STR_2680 :All research complete
|
||||
STR_2684 :Large group of guests arrive
|
||||
STR_2685 :Simplex Noise Parameters
|
||||
STR_2686 :Low:
|
||||
STR_2687 :High:
|
||||
STR_2686 :Min. land height:
|
||||
STR_2687 :Max. land height:
|
||||
STR_2688 :Base Frequency:
|
||||
STR_2689 :Octaves:
|
||||
STR_2690 :Map Generation
|
||||
@@ -3166,9 +3166,9 @@ STR_6041 :{BLACK}No mechanics are hired!
|
||||
STR_6042 :Load height map
|
||||
STR_6043 :Select height map
|
||||
STR_6044 :Smooth height map
|
||||
STR_6045 :Strength
|
||||
STR_6045 :Strength:
|
||||
STR_6046 :Normalise height map
|
||||
STR_6047 :Smooth tiles
|
||||
STR_6047 :Smooth tile edges
|
||||
STR_6048 :Height map error
|
||||
STR_6049 :Error reading PNG
|
||||
STR_6050 :Error reading bitmap
|
||||
@@ -3748,6 +3748,39 @@ STR_6673 :Transparent
|
||||
STR_6674 :{MONTH}, Year {COMMA16}
|
||||
STR_6675 :Peep Names
|
||||
STR_6676 :At least one peep names object must be selected
|
||||
STR_6677 :Add beaches around water bodies
|
||||
STR_6678 :Heightmap source:
|
||||
STR_6679 :Flatland
|
||||
STR_6680 :Simplex noise
|
||||
STR_6681 :Heightmap file
|
||||
STR_6682 :Map Generator - Generator
|
||||
STR_6683 :Map Generator - Terrain
|
||||
STR_6684 :Map Generator - Water
|
||||
STR_6685 :Map Generator - Forests
|
||||
STR_6686 :Tree to land ratio:
|
||||
STR_6687 :Min. tree altitude:
|
||||
STR_6688 :Max. tree altitude:
|
||||
STR_6689 :{UINT16}%
|
||||
STR_6690 :Minimum land height
|
||||
STR_6691 :Enter min. land height between {COMMA16} and {COMMA16}
|
||||
STR_6692 :Maximum land height
|
||||
STR_6693 :Enter max. land height between {COMMA16} and {COMMA16}
|
||||
STR_6694 :Minimum tree altitude
|
||||
STR_6695 :Enter min. tree altitude between {COMMA16} and {COMMA16}
|
||||
STR_6696 :Maximum tree altitude
|
||||
STR_6697 :Enter max. tree altitude between {COMMA16} and {COMMA16}
|
||||
STR_6698 :Tree to land ratio
|
||||
STR_6699 :Enter tree to land ratio between {COMMA16} and {COMMA16}
|
||||
STR_6700 :Simplex Base Frequency
|
||||
STR_6701 :Enter Base Frequency between {COMMA2DP32} and {COMMA2DP32}
|
||||
STR_6702 :Simplex Octaves
|
||||
STR_6703 :Enter Octaves between {COMMA16} and {COMMA16}
|
||||
STR_6704 :{COMMA2DP32}
|
||||
STR_6705 :Browse...
|
||||
STR_6706 :{WINDOW_COLOUR_2}Current image file: {BLACK}{STRING}
|
||||
STR_6707 :(none selected)
|
||||
STR_6708 :Smooth Strength
|
||||
STR_6709 :Enter Smooth Strength between {COMMA16} and {COMMA16}
|
||||
|
||||
#############
|
||||
# Scenarios #
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
- Feature: [#22694] Park graphs have tooltips and can be resized like finance graphs.
|
||||
- Feature: [#22758] The table of ‘real’ peep names can now be changed using Peep Names objects.
|
||||
- Improved: [#22470] Android: automatically detect RCT2 installs in /sdcard/rct2.
|
||||
- Improved: [#22735] The map generator has a redesigned interface that is much more user friendly.
|
||||
- Improved: [#22777] Add long flat-to-steep track pieces to the Wooden and Classic Wooden Roller Coasters.
|
||||
- Improved: [#22815] Add long flat-to-steep track pieces to the Mine Train Coaster.
|
||||
- Change: [#22494] Themes window now displays colours and checkboxes vertically.
|
||||
|
||||
BIN
resources/g2/icons/map_gen_terrain_tab.png
Normal file
BIN
resources/g2/icons/map_gen_terrain_tab.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.0 KiB |
@@ -307,6 +307,11 @@
|
||||
"x": 2,
|
||||
"y": 2
|
||||
},
|
||||
{
|
||||
"path": "icons/map_gen_terrain_tab.png",
|
||||
"x": 1,
|
||||
"y": 10
|
||||
},
|
||||
{
|
||||
"path": "icons/map_gen_button.png"
|
||||
},
|
||||
|
||||
@@ -19,6 +19,7 @@ namespace OpenRCT2
|
||||
STR_ADJUST_LARGER_LAND_TIP = 2379,
|
||||
STR_ADJUST_SMALLER_LAND_TIP = 2378,
|
||||
STR_BROKEN = 3124,
|
||||
STR_BROWSE = 6705,
|
||||
STR_CANCEL = 972,
|
||||
STR_CANT_INCREASE_FURTHER = 3264,
|
||||
STR_CANT_REDUCE_FURTHER = 3265,
|
||||
@@ -36,6 +37,7 @@ namespace OpenRCT2
|
||||
STR_ENTER_PARK_NAME = 1719,
|
||||
STR_ENTER_SELECTION_SIZE = 5129,
|
||||
STR_ERROR_INVALID_CHARACTERS = 5243,
|
||||
STR_FORMAT_COMMA2DP32 = 6704,
|
||||
STR_FORMAT_INTEGER = 5182,
|
||||
STR_GRAPH_LABEL = 2222,
|
||||
STR_LIGHTPINK_STRINGID = 5299,
|
||||
@@ -896,25 +898,55 @@ namespace OpenRCT2
|
||||
STR_SHOW_RIDES_STALLS_ON_MAP_TIP = 3144,
|
||||
|
||||
// Window: MapGen
|
||||
STR_BASE_HEIGHT = 5183,
|
||||
STR_BASE_HEIGHT_LABEL = 2691,
|
||||
STR_ENTER_BASE_HEIGHT = 5184,
|
||||
STR_BASE_HEIGHT = 5183, // unused
|
||||
STR_BASE_HEIGHT_LABEL = 2691, // unused
|
||||
STR_BEACHES_WATER_BODIES = 6677,
|
||||
STR_ENTER_BASE_FREQUENCY = 6701,
|
||||
STR_ENTER_BASE_HEIGHT = 5184, // unused
|
||||
STR_ENTER_MAX_LAND = 6693,
|
||||
STR_ENTER_MAX_TREE_ALTITUDE = 6697,
|
||||
STR_ENTER_MIN_LAND = 6691,
|
||||
STR_ENTER_MIN_TREE_ALTITUDE = 6695,
|
||||
STR_ENTER_OCTAVES = 6703,
|
||||
STR_ENTER_SMOOTH_STRENGTH = 6708,
|
||||
STR_ENTER_TREE_TO_LAND_RATIO = 6699,
|
||||
STR_ENTER_WATER_LEVEL = 5186,
|
||||
STR_HEIGHTMAP_FILE = 6681,
|
||||
STR_HEIGHTMAP_FLATLAND = 6679,
|
||||
STR_HEIGHTMAP_SIMPLEX_NOISE = 6680,
|
||||
STR_HEIGHTMAP_SOURCE = 6678,
|
||||
STR_MAPGEN_ACTION_GENERATE = 2694,
|
||||
STR_MAPGEN_CAPTION_FORESTS = 6685,
|
||||
STR_MAPGEN_CAPTION_GENERATOR = 6682,
|
||||
STR_MAPGEN_CAPTION_TERRAIN = 6683,
|
||||
STR_MAPGEN_CAPTION_WATER = 6684,
|
||||
STR_MAPGEN_CURRENT_HEIGHTMAP_FILE = 6706,
|
||||
STR_MAPGEN_MAX_LAND_HEIGHT = 2687,
|
||||
STR_MAPGEN_MIN_LAND_HEIGHT = 2686,
|
||||
STR_MAPGEN_NONE_SELECTED = 6707,
|
||||
STR_MAPGEN_NORMALIZE = 6046,
|
||||
STR_MAPGEN_OPTION_PLACE_TREES = 2696,
|
||||
STR_MAPGEN_OPTION_RANDOM_TERRAIN = 2695,
|
||||
STR_MAPGEN_SELECT_HEIGHTMAP = 6043,
|
||||
STR_MAPGEN_SIMPLEX_NOISE = 2685,
|
||||
STR_MAPGEN_SIMPLEX_NOISE_BASE_FREQUENCY = 2688,
|
||||
STR_MAPGEN_SIMPLEX_NOISE_HIGH = 2687,
|
||||
STR_MAPGEN_SIMPLEX_NOISE_LOW_ = 2686,
|
||||
STR_MAPGEN_SIMPLEX_NOISE_OCTAVES = 2689,
|
||||
STR_MAPGEN_SMOOTH_HEIGHTMAP = 6044,
|
||||
STR_MAPGEN_SMOOTH_STRENGTH = 6045,
|
||||
STR_MAPGEN_SMOOTH_TILE = 6047,
|
||||
STR_MAPGEN_WINDOW_TITLE = 2690,
|
||||
STR_MAPGEN_TREE_MAX_ALTITUDE = 6688,
|
||||
STR_MAPGEN_TREE_MIN_ALTITUDE = 6687,
|
||||
STR_MAPGEN_TREE_TO_LAND_RATIO = 6686,
|
||||
STR_MAPGEN_TREE_TO_LAND_RATIO_PCT = 6689,
|
||||
STR_MAX_LAND_HEIGHT = 6692,
|
||||
STR_MAX_TREE_ALTITUDE = 6696,
|
||||
STR_MIN_LAND_HEIGHT = 6690,
|
||||
STR_MIN_TREE_ALTITUDE = 6694,
|
||||
STR_SIMPLEX_BASE_FREQUENCY = 6700,
|
||||
STR_SIMPLEX_OCTAVES = 6702,
|
||||
STR_SMOOTH_STRENGTH = 6709,
|
||||
STR_TERRAIN_LABEL = 2693,
|
||||
STR_TREE_TO_LAND_RATIO = 6694,
|
||||
STR_WATER_LEVEL = 5185,
|
||||
STR_WATER_LEVEL_LABEL = 2692,
|
||||
|
||||
@@ -2132,6 +2164,7 @@ namespace OpenRCT2
|
||||
STR_HEIGHT_MARKS_ON_RIDE_TRACKS = 1153,
|
||||
STR_HIGHLIGHT_PATH_ISSUES_MENU = 6219,
|
||||
STR_LOAD_LANDSCAPE = 884,
|
||||
STR_MAPGEN_MENU_ITEM = 2690,
|
||||
STR_MULTIPLAYER_RECONNECT = 6309,
|
||||
STR_NEW_GAME = 5715,
|
||||
STR_OVERLAY_CLEARANCE_CHECKS_DISABLED = 6374,
|
||||
|
||||
@@ -165,7 +165,7 @@ namespace OpenRCT2::Ui
|
||||
const int16_t xPos = origin.x + size.width - 11;
|
||||
const int16_t yPos = origin.y + 1;
|
||||
const uint16_t width = 11;
|
||||
const uint16_t height = 10;
|
||||
const uint16_t height = size.height - 2;
|
||||
|
||||
return MakeWidget({ xPos, yPos }, { width, height }, WindowWidgetType::Button, colour, STR_DROPDOWN_GLYPH, tooltip);
|
||||
}
|
||||
|
||||
@@ -199,6 +199,7 @@ namespace OpenRCT2::Ui::Windows
|
||||
else
|
||||
{
|
||||
ContextOpenWindow(WindowClass::Map);
|
||||
ContextOpenWindow(WindowClass::Mapgen);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1173,7 +1173,7 @@ namespace OpenRCT2::Ui::Windows
|
||||
gDropdownItems[i++].Format = STR_EXTRA_VIEWPORT;
|
||||
if ((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && GetGameState().EditorStep == EditorStep::LandscapeEditor)
|
||||
{
|
||||
gDropdownItems[i++].Format = STR_MAPGEN_WINDOW_TITLE;
|
||||
gDropdownItems[i++].Format = STR_MAPGEN_MENU_ITEM;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
|
||||
@@ -1420,7 +1420,7 @@ enum : StringId
|
||||
STR_ERROR_READING_PNG = 6049,
|
||||
STR_ERROR_READING_BITMAP = 6050,
|
||||
|
||||
STR_ERROR_HEIHGT_MAP_TOO_BIG = 6052,
|
||||
STR_ERROR_HEIGHT_MAP_TOO_BIG = 6052,
|
||||
STR_ERROR_CANNOT_NORMALIZE = 6053,
|
||||
STR_ERROR_24_BIT_BITMAP = 6054,
|
||||
|
||||
|
||||
@@ -969,6 +969,7 @@ enum : ImageIndex
|
||||
SPR_G2_ARROW_UP,
|
||||
SPR_G2_ARROW_DOWN,
|
||||
SPR_G2_RELOAD,
|
||||
SPR_G2_MAP_GEN_TERRAIN_TAB,
|
||||
SPR_G2_MAP_GEN_BTN,
|
||||
SPR_G2_PEEP_SPAWN,
|
||||
|
||||
|
||||
@@ -39,6 +39,11 @@ int32_t MetresToFeet(int32_t metres)
|
||||
return (metres * 840) / 256;
|
||||
}
|
||||
|
||||
int32_t FeetToMetres(int32_t feet)
|
||||
{
|
||||
return feet * 256 / 840;
|
||||
}
|
||||
|
||||
int32_t MphToKmph(int32_t mph)
|
||||
{
|
||||
// 1 mph = 1.60934 kmph
|
||||
@@ -52,6 +57,16 @@ int32_t MphToDmps(int32_t mph)
|
||||
return (mph * 73243) >> 14;
|
||||
}
|
||||
|
||||
int32_t BaseZToMetres(int16_t baseZ)
|
||||
{
|
||||
return (baseZ / 2 - 7) * 1.5;
|
||||
}
|
||||
|
||||
uint8_t MetresToBaseZ(int16_t metres)
|
||||
{
|
||||
return ((metres / 1.5) + 7) * 2;
|
||||
}
|
||||
|
||||
/* Case insensitive logical compare */
|
||||
// Example:
|
||||
// - Guest 10
|
||||
|
||||
@@ -25,8 +25,11 @@
|
||||
|
||||
int32_t SquaredMetresToSquaredFeet(int32_t squaredMetres);
|
||||
int32_t MetresToFeet(int32_t metres);
|
||||
int32_t FeetToMetres(int32_t feet);
|
||||
int32_t MphToKmph(int32_t mph);
|
||||
int32_t MphToDmps(int32_t mph);
|
||||
int32_t BaseZToMetres(int16_t baseZ);
|
||||
uint8_t MetresToBaseZ(int16_t metres);
|
||||
|
||||
inline int32_t UtilBitScanForward(uint32_t source)
|
||||
{
|
||||
|
||||
@@ -93,7 +93,40 @@ static constexpr std::string_view BaseTerrain[] = {
|
||||
"rct2.terrain_surface.dirt", "rct2.terrain_surface.ice",
|
||||
};
|
||||
|
||||
static void MapGenPlaceTrees();
|
||||
static void MapGenGenerateBlank(MapGenSettings* settings);
|
||||
static void MapGenGenerateSimplex(MapGenSettings* settings);
|
||||
static void MapGenGenerateFromHeightmapImage(MapGenSettings* settings);
|
||||
|
||||
static void MapGenPlaceTrees(MapGenSettings* settings);
|
||||
static void MapGenAddBeaches(MapGenSettings* settings);
|
||||
|
||||
void MapGenGenerate(MapGenSettings* settings)
|
||||
{
|
||||
// First, generate the height map
|
||||
switch (settings->algorithm)
|
||||
{
|
||||
case MapGenAlgorithm::blank:
|
||||
MapGenGenerateBlank(settings);
|
||||
break;
|
||||
|
||||
case MapGenAlgorithm::simplexNoise:
|
||||
MapGenGenerateSimplex(settings);
|
||||
break;
|
||||
|
||||
case MapGenAlgorithm::heightmapImage:
|
||||
MapGenGenerateFromHeightmapImage(settings);
|
||||
break;
|
||||
}
|
||||
|
||||
// Add beaches?
|
||||
if (settings->beaches)
|
||||
MapGenAddBeaches(settings);
|
||||
|
||||
// Place trees?
|
||||
if (settings->trees)
|
||||
MapGenPlaceTrees(settings);
|
||||
}
|
||||
|
||||
static void MapGenSetWaterLevel(int32_t waterLevel);
|
||||
static void MapGenSmoothHeight(int32_t iterations);
|
||||
static void MapGenSetHeight(MapGenSettings* settings);
|
||||
@@ -118,41 +151,14 @@ static void SetHeight(int32_t x, int32_t y, int32_t height)
|
||||
_height[x + y * _heightSize.x] = height;
|
||||
}
|
||||
|
||||
void MapGenGenerateBlank(MapGenSettings* settings)
|
||||
static ObjectEntryIndex MapGenSurfaceTextureId(MapGenSettings* settings)
|
||||
{
|
||||
int32_t x, y;
|
||||
MapClearAllElements();
|
||||
|
||||
MapInit(settings->mapSize);
|
||||
for (y = 1; y < settings->mapSize.y - 1; y++)
|
||||
{
|
||||
for (x = 1; x < settings->mapSize.x - 1; x++)
|
||||
{
|
||||
auto surfaceElement = MapGetSurfaceElementAt(TileCoordsXY{ x, y });
|
||||
if (surfaceElement != nullptr)
|
||||
{
|
||||
surfaceElement->SetSurfaceObjectIndex(settings->floor);
|
||||
surfaceElement->SetEdgeObjectIndex(settings->wall);
|
||||
surfaceElement->BaseHeight = settings->height;
|
||||
surfaceElement->ClearanceHeight = settings->height;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MapGenSetWaterLevel(settings->water_level);
|
||||
}
|
||||
|
||||
void MapGenGenerate(MapGenSettings* settings)
|
||||
{
|
||||
const auto& mapSize = settings->mapSize;
|
||||
auto waterLevel = settings->water_level;
|
||||
const auto selectedFloor = TerrainSurfaceObject::GetById(settings->floor);
|
||||
std::string_view floorTexture = selectedFloor != nullptr ? selectedFloor->GetIdentifier() : "";
|
||||
const auto selectedEdge = TerrainEdgeObject::GetById(settings->wall);
|
||||
std::string_view edgeTexture = selectedFloor != nullptr ? selectedEdge->GetIdentifier() : "";
|
||||
auto& objectManager = OpenRCT2::GetContext()->GetObjectManager();
|
||||
|
||||
if (floorTexture.empty())
|
||||
const auto selectedFloor = TerrainSurfaceObject::GetById(settings->landTexture);
|
||||
std::string_view surfaceTexture = selectedFloor != nullptr ? selectedFloor->GetIdentifier() : "";
|
||||
|
||||
if (surfaceTexture.empty())
|
||||
{
|
||||
std::vector<std::string_view> availableTerrains;
|
||||
std::copy_if(std::begin(BaseTerrain), std::end(BaseTerrain), std::back_inserter(availableTerrains), [&](auto terrain) {
|
||||
@@ -161,17 +167,31 @@ void MapGenGenerate(MapGenSettings* settings)
|
||||
|
||||
if (availableTerrains.empty())
|
||||
// Fall back to the first available surface texture that is available in the park
|
||||
floorTexture = TerrainSurfaceObject::GetById(0)->GetIdentifier();
|
||||
surfaceTexture = TerrainSurfaceObject::GetById(0)->GetIdentifier();
|
||||
else
|
||||
floorTexture = availableTerrains[UtilRand() % availableTerrains.size()];
|
||||
surfaceTexture = availableTerrains[UtilRand() % availableTerrains.size()];
|
||||
}
|
||||
|
||||
auto surfaceTextureId = objectManager.GetLoadedObjectEntryIndex(ObjectEntryDescriptor(surfaceTexture));
|
||||
return surfaceTextureId;
|
||||
}
|
||||
|
||||
static ObjectEntryIndex MapGenEdgeTextureId(MapGenSettings* settings, ObjectEntryIndex surfaceTextureId)
|
||||
{
|
||||
auto& objectManager = OpenRCT2::GetContext()->GetObjectManager();
|
||||
|
||||
const auto selectedEdge = TerrainEdgeObject::GetById(settings->edgeTexture);
|
||||
std::string_view edgeTexture = selectedEdge != nullptr ? selectedEdge->GetIdentifier() : "";
|
||||
|
||||
if (edgeTexture.empty())
|
||||
{
|
||||
auto surfaceObject = objectManager.GetLoadedObject(ObjectType::TerrainSurface, surfaceTextureId);
|
||||
auto surfaceTexture = surfaceObject->GetIdentifier();
|
||||
|
||||
// Base edge type on surface type
|
||||
if (floorTexture == "rct2.terrain_surface.dirt")
|
||||
if (surfaceTexture == "rct2.terrain_surface.dirt")
|
||||
edgeTexture = "rct2.terrain_edge.wood_red";
|
||||
else if (floorTexture == "rct2.terrain_surface.ice")
|
||||
else if (surfaceTexture == "rct2.terrain_surface.ice")
|
||||
edgeTexture = "rct2.terrain_edge.ice";
|
||||
else
|
||||
edgeTexture = "rct2.terrain_edge.rock";
|
||||
@@ -181,29 +201,46 @@ void MapGenGenerate(MapGenSettings* settings)
|
||||
edgeTexture = TerrainEdgeObject::GetById(0)->GetIdentifier();
|
||||
}
|
||||
|
||||
auto floorTextureId = objectManager.GetLoadedObjectEntryIndex(ObjectEntryDescriptor(floorTexture));
|
||||
auto edgeTextureId = objectManager.GetLoadedObjectEntryIndex(ObjectEntryDescriptor(edgeTexture));
|
||||
return edgeTextureId;
|
||||
}
|
||||
|
||||
static void MapGenResetSurfaces(MapGenSettings* settings)
|
||||
{
|
||||
MapClearAllElements();
|
||||
MapInit(settings->mapSize);
|
||||
|
||||
// Initialise the base map
|
||||
MapInit(mapSize);
|
||||
for (auto y = 1; y < mapSize.y - 1; y++)
|
||||
const auto surfaceTextureId = MapGenSurfaceTextureId(settings);
|
||||
const auto edgeTextureId = MapGenEdgeTextureId(settings, surfaceTextureId);
|
||||
|
||||
for (auto y = 1; y < settings->mapSize.y - 1; y++)
|
||||
{
|
||||
for (auto x = 1; x < mapSize.x - 1; x++)
|
||||
for (auto x = 1; x < settings->mapSize.x - 1; x++)
|
||||
{
|
||||
auto surfaceElement = MapGetSurfaceElementAt(TileCoordsXY{ x, y });
|
||||
if (surfaceElement != nullptr)
|
||||
{
|
||||
surfaceElement->SetSurfaceObjectIndex(floorTextureId);
|
||||
surfaceElement->SetSurfaceObjectIndex(surfaceTextureId);
|
||||
surfaceElement->SetEdgeObjectIndex(edgeTextureId);
|
||||
surfaceElement->BaseHeight = settings->height;
|
||||
surfaceElement->ClearanceHeight = settings->height;
|
||||
surfaceElement->BaseHeight = settings->heightmapLow;
|
||||
surfaceElement->ClearanceHeight = settings->heightmapLow;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void MapGenGenerateBlank(MapGenSettings* settings)
|
||||
{
|
||||
MapGenResetSurfaces(settings);
|
||||
MapGenSetWaterLevel(settings->waterLevel);
|
||||
}
|
||||
|
||||
static void MapGenGenerateSimplex(MapGenSettings* settings)
|
||||
{
|
||||
MapGenResetSurfaces(settings);
|
||||
|
||||
// Create the temporary height map and initialise
|
||||
auto& mapSize = settings->mapSize;
|
||||
_heightSize = { mapSize.x * 2, mapSize.y * 2 };
|
||||
_height = new uint8_t[_heightSize.y * _heightSize.x];
|
||||
std::fill_n(_height, _heightSize.y * _heightSize.x, 0x00);
|
||||
@@ -215,43 +252,47 @@ void MapGenGenerate(MapGenSettings* settings)
|
||||
MapGenSetHeight(settings);
|
||||
delete[] _height;
|
||||
|
||||
// Set the tile slopes so that there are no cliffs
|
||||
while (MapSmooth(1, 1, mapSize.x - 1, mapSize.y - 1))
|
||||
if (settings->smoothTileEdges)
|
||||
{
|
||||
// Set the tile slopes so that there are no cliffs
|
||||
while (MapSmooth(1, 1, mapSize.x - 1, mapSize.y - 1))
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
// Add the water
|
||||
MapGenSetWaterLevel(waterLevel);
|
||||
MapGenSetWaterLevel(settings->waterLevel);
|
||||
}
|
||||
|
||||
// Add sandy beaches
|
||||
std::string_view beachTexture = floorTexture;
|
||||
if (settings->floor == -1 && floorTexture == "rct2.terrain_surface.grass" && (UtilRand() & 1))
|
||||
{
|
||||
std::vector<std::string_view> availableBeachTextures;
|
||||
if (objectManager.GetLoadedObject(ObjectEntryDescriptor("rct2.terrain_surface.sand")) != nullptr)
|
||||
availableBeachTextures.push_back("rct2.terrain_surface.sand");
|
||||
if (objectManager.GetLoadedObject(ObjectEntryDescriptor("rct2.terrain_surface.sand_brown")) != nullptr)
|
||||
availableBeachTextures.push_back("rct2.terrain_surface.sand_brown");
|
||||
static void MapGenAddBeaches(MapGenSettings* settings)
|
||||
{
|
||||
auto& objectManager = OpenRCT2::GetContext()->GetObjectManager();
|
||||
|
||||
if (!availableBeachTextures.empty())
|
||||
beachTexture = availableBeachTextures[UtilRand() % availableBeachTextures.size()];
|
||||
}
|
||||
// Figure out what beach texture to use
|
||||
std::vector<std::string_view> availableBeachTextures;
|
||||
if (objectManager.GetLoadedObject(ObjectEntryDescriptor("rct2.terrain_surface.sand")) != nullptr)
|
||||
availableBeachTextures.push_back("rct2.terrain_surface.sand");
|
||||
if (objectManager.GetLoadedObject(ObjectEntryDescriptor("rct2.terrain_surface.sand_brown")) != nullptr)
|
||||
availableBeachTextures.push_back("rct2.terrain_surface.sand_brown");
|
||||
|
||||
if (availableBeachTextures.empty())
|
||||
return;
|
||||
|
||||
std::string_view beachTexture = availableBeachTextures[UtilRand() % availableBeachTextures.size()];
|
||||
auto beachTextureId = objectManager.GetLoadedObjectEntryIndex(ObjectEntryDescriptor(beachTexture));
|
||||
|
||||
// Add sandy beaches
|
||||
const auto& mapSize = settings->mapSize;
|
||||
for (auto y = 1; y < mapSize.y - 1; y++)
|
||||
{
|
||||
for (auto x = 1; x < mapSize.x - 1; x++)
|
||||
{
|
||||
auto surfaceElement = MapGetSurfaceElementAt(TileCoordsXY{ x, y });
|
||||
|
||||
if (surfaceElement != nullptr && surfaceElement->BaseHeight < waterLevel + 6)
|
||||
if (surfaceElement != nullptr && surfaceElement->BaseHeight < settings->waterLevel + 6)
|
||||
surfaceElement->SetSurfaceObjectIndex(beachTextureId);
|
||||
}
|
||||
}
|
||||
|
||||
// Place the trees
|
||||
if (settings->trees != 0)
|
||||
MapGenPlaceTrees();
|
||||
}
|
||||
|
||||
static void MapGenPlaceTree(ObjectEntryIndex type, const CoordsXY& loc)
|
||||
@@ -306,7 +347,7 @@ template<typename T> static bool TryFindTreeInList(std::string_view id, const T&
|
||||
/**
|
||||
* Randomly places a selection of preset trees on the map. Picks the right tree for the terrain it is placing it on.
|
||||
*/
|
||||
static void MapGenPlaceTrees()
|
||||
static void MapGenPlaceTrees(MapGenSettings* settings)
|
||||
{
|
||||
std::vector<int32_t> grassTreeIds;
|
||||
std::vector<int32_t> desertTreeIds;
|
||||
@@ -335,16 +376,14 @@ static void MapGenPlaceTrees()
|
||||
}
|
||||
|
||||
// Place trees
|
||||
CoordsXY pos;
|
||||
float treeToLandRatio = (10 + (UtilRand() % 30)) / 100.0f;
|
||||
float treeToLandRatio = static_cast<float>(settings->treeToLandRatio) / 100.0f;
|
||||
|
||||
auto& gameState = GetGameState();
|
||||
for (int32_t y = 1; y < gameState.MapSize.y - 1; y++)
|
||||
{
|
||||
for (int32_t x = 1; x < gameState.MapSize.x - 1; x++)
|
||||
{
|
||||
pos.x = x * kCoordsXYStep;
|
||||
pos.y = y * kCoordsXYStep;
|
||||
|
||||
auto pos = CoordsXY{ x, y } * kCoordsXYStep;
|
||||
auto* surfaceElement = MapGetSurfaceElementAt(pos);
|
||||
if (surfaceElement == nullptr)
|
||||
continue;
|
||||
@@ -353,6 +392,10 @@ static void MapGenPlaceTrees()
|
||||
if (surfaceElement->GetWaterHeight() > 0)
|
||||
continue;
|
||||
|
||||
if (settings->minTreeAltitude > surfaceElement->BaseHeight
|
||||
|| settings->maxTreeAltitude < surfaceElement->BaseHeight)
|
||||
continue;
|
||||
|
||||
// On sand surfaces, give the tile a score based on nearby water, to be used to determine whether to spawn
|
||||
// vegetation
|
||||
float oasisScore = 0.0f;
|
||||
@@ -490,7 +533,7 @@ static void MapGenSetHeight(MapGenSettings* settings)
|
||||
surfaceElement->BaseHeight = std::max(2, baseHeight * 2);
|
||||
|
||||
// If base height is below water level, lower it to create more natural shorelines
|
||||
if (surfaceElement->BaseHeight >= 4 && surfaceElement->BaseHeight <= settings->water_level)
|
||||
if (surfaceElement->BaseHeight >= 4 && surfaceElement->BaseHeight <= settings->waterLevel)
|
||||
surfaceElement->BaseHeight -= 2;
|
||||
|
||||
surfaceElement->ClearanceHeight = surfaceElement->BaseHeight;
|
||||
@@ -648,18 +691,16 @@ static float Grad(int32_t hash, float x, float y)
|
||||
|
||||
static void MapGenSimplex(MapGenSettings* settings)
|
||||
{
|
||||
int32_t x, y;
|
||||
|
||||
float freq = settings->simplex_base_freq * (1.0f / _heightSize.x);
|
||||
float freq = settings->simplex_base_freq / 100.0f * (1.0f / _heightSize.x);
|
||||
int32_t octaves = settings->simplex_octaves;
|
||||
|
||||
int32_t low = settings->simplex_low;
|
||||
int32_t high = settings->simplex_high;
|
||||
int32_t low = settings->heightmapLow / 2;
|
||||
int32_t high = settings->heightmapHigh / 2 - low;
|
||||
|
||||
NoiseRand();
|
||||
for (y = 0; y < _heightSize.y; y++)
|
||||
for (int32_t y = 0; y < _heightSize.y; y++)
|
||||
{
|
||||
for (x = 0; x < _heightSize.x; x++)
|
||||
for (int32_t x = 0; x < _heightSize.x; x++)
|
||||
{
|
||||
float noiseValue = std::clamp(FractalNoise(x, y, freq, octaves, 2.0f, 0.65f), -1.0f, 1.0f);
|
||||
float normalisedNoiseValue = (noiseValue + 1.0f) / 2.0f;
|
||||
@@ -682,7 +723,7 @@ static TileCoordsXY MapgenHeightmapCoordToTileCoordsXY(uint32_t x, uint32_t y)
|
||||
return TileCoordsXY(static_cast<int32_t>(y + 1), static_cast<int32_t>(x + 1));
|
||||
}
|
||||
|
||||
bool MapGenLoadHeightmap(const utf8* path)
|
||||
bool MapGenLoadHeightmapImage(const utf8* path)
|
||||
{
|
||||
auto format = Imaging::GetImageFormatFromPath(path);
|
||||
if (format == IMAGE_FORMAT::PNG)
|
||||
@@ -698,7 +739,7 @@ bool MapGenLoadHeightmap(const utf8* path)
|
||||
auto height = std::min<uint32_t>(image.Height, kMaximumMapSizePractical);
|
||||
if (width != image.Width || height != image.Height)
|
||||
{
|
||||
ContextShowError(STR_HEIGHT_MAP_ERROR, STR_ERROR_HEIHGT_MAP_TOO_BIG, {});
|
||||
ContextShowError(STR_HEIGHT_MAP_ERROR, STR_ERROR_HEIGHT_MAP_TOO_BIG, {});
|
||||
}
|
||||
|
||||
// Allocate memory for the height map values, one byte pixel
|
||||
@@ -743,7 +784,7 @@ bool MapGenLoadHeightmap(const utf8* path)
|
||||
/**
|
||||
* Frees the memory used to store the selected height map
|
||||
*/
|
||||
void MapGenUnloadHeightmap()
|
||||
void MapGenUnloadHeightmapImage()
|
||||
{
|
||||
_heightMapData.mono_bitmap.clear();
|
||||
_heightMapData.width = 0;
|
||||
@@ -796,10 +837,10 @@ static void MapGenSmoothHeightmap(std::vector<uint8_t>& src, int32_t strength)
|
||||
}
|
||||
}
|
||||
|
||||
void MapGenGenerateFromHeightmap(MapGenSettings* settings)
|
||||
static void MapGenGenerateFromHeightmapImage(MapGenSettings* settings)
|
||||
{
|
||||
Guard::Assert(!_heightMapData.mono_bitmap.empty(), "No height map loaded");
|
||||
Guard::Assert(settings->simplex_high != settings->simplex_low, "Low and high setting cannot be the same");
|
||||
Guard::Assert(settings->heightmapHigh != settings->heightmapLow, "Low and high setting cannot be the same");
|
||||
|
||||
// Make a copy of the original height map that we can edit
|
||||
auto dest = _heightMapData.mono_bitmap;
|
||||
@@ -840,10 +881,13 @@ void MapGenGenerateFromHeightmap(MapGenSettings* settings)
|
||||
}
|
||||
|
||||
Guard::Assert(maxValue > minValue, "Input range is invalid");
|
||||
Guard::Assert(settings->simplex_high > settings->simplex_low, "Output range is invalid");
|
||||
Guard::Assert(settings->heightmapHigh > settings->heightmapLow, "Output range is invalid");
|
||||
|
||||
const auto surfaceTextureId = MapGenSurfaceTextureId(settings);
|
||||
const auto edgeTextureId = MapGenEdgeTextureId(settings, surfaceTextureId);
|
||||
|
||||
const uint8_t rangeIn = maxValue - minValue;
|
||||
const uint8_t rangeOut = settings->simplex_high - settings->simplex_low;
|
||||
const uint8_t rangeOut = (settings->heightmapHigh - settings->heightmapLow) * 2;
|
||||
|
||||
for (uint32_t y = 0; y < _heightMapData.height; y++)
|
||||
{
|
||||
@@ -857,7 +901,8 @@ void MapGenGenerateFromHeightmap(MapGenSettings* settings)
|
||||
|
||||
// Read value from bitmap, and convert its range
|
||||
uint8_t value = dest[x + y * _heightMapData.width];
|
||||
value = static_cast<uint8_t>(static_cast<float>(value - minValue) / rangeIn * rangeOut) + settings->simplex_low;
|
||||
value = static_cast<uint8_t>(static_cast<float>(value - minValue) / rangeIn * rangeOut)
|
||||
+ (settings->heightmapLow * 2);
|
||||
surfaceElement->BaseHeight = value;
|
||||
|
||||
// Floor to even number
|
||||
@@ -865,16 +910,20 @@ void MapGenGenerateFromHeightmap(MapGenSettings* settings)
|
||||
surfaceElement->BaseHeight *= 2;
|
||||
surfaceElement->ClearanceHeight = surfaceElement->BaseHeight;
|
||||
|
||||
// Set textures
|
||||
surfaceElement->SetSurfaceObjectIndex(surfaceTextureId);
|
||||
surfaceElement->SetEdgeObjectIndex(edgeTextureId);
|
||||
|
||||
// Set water level
|
||||
if (surfaceElement->BaseHeight < settings->water_level)
|
||||
if (surfaceElement->BaseHeight < settings->waterLevel)
|
||||
{
|
||||
surfaceElement->SetWaterHeight(settings->water_level * kCoordsZStep);
|
||||
surfaceElement->SetWaterHeight(settings->waterLevel * kCoordsZStep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Smooth map
|
||||
if (settings->smooth)
|
||||
// Smooth tile edges
|
||||
if (settings->smoothTileEdges)
|
||||
{
|
||||
// Keep smoothing the entire map until no tiles are changed anymore
|
||||
while (true)
|
||||
|
||||
@@ -12,33 +12,42 @@
|
||||
#include "../core/StringTypes.h"
|
||||
#include "Location.hpp"
|
||||
|
||||
enum class MapGenAlgorithm : uint8_t
|
||||
{
|
||||
blank,
|
||||
simplexNoise,
|
||||
heightmapImage,
|
||||
};
|
||||
|
||||
struct MapGenSettings
|
||||
{
|
||||
// Base
|
||||
TileCoordsXY mapSize;
|
||||
int32_t height;
|
||||
int32_t water_level;
|
||||
int32_t floor;
|
||||
int32_t wall;
|
||||
MapGenAlgorithm algorithm = MapGenAlgorithm::blank;
|
||||
TileCoordsXY mapSize{ 150, 150 };
|
||||
int32_t waterLevel = 6;
|
||||
int32_t landTexture = 0;
|
||||
int32_t edgeTexture = 0;
|
||||
int32_t heightmapLow = 14;
|
||||
int32_t heightmapHigh = 60;
|
||||
bool smoothTileEdges = true;
|
||||
|
||||
// Features (e.g. tree, rivers, lakes etc.)
|
||||
int32_t trees;
|
||||
bool trees = true;
|
||||
int32_t treeToLandRatio = 25;
|
||||
int32_t minTreeAltitude = 10;
|
||||
int32_t maxTreeAltitude = 50;
|
||||
bool beaches = true;
|
||||
|
||||
// Simplex Noise Parameters
|
||||
int32_t simplex_low;
|
||||
int32_t simplex_high;
|
||||
float simplex_base_freq;
|
||||
int32_t simplex_octaves;
|
||||
int32_t simplex_base_freq = 175;
|
||||
int32_t simplex_octaves = 6;
|
||||
|
||||
// Height map settings
|
||||
bool smooth;
|
||||
bool smooth_height_map;
|
||||
uint32_t smooth_strength;
|
||||
bool normalize_height;
|
||||
bool smooth_height_map = true;
|
||||
uint32_t smooth_strength = 1;
|
||||
bool normalize_height = true;
|
||||
};
|
||||
|
||||
void MapGenGenerateBlank(MapGenSettings* settings);
|
||||
void MapGenGenerate(MapGenSettings* settings);
|
||||
bool MapGenLoadHeightmap(const utf8* path);
|
||||
void MapGenUnloadHeightmap();
|
||||
void MapGenGenerateFromHeightmap(MapGenSettings* settings);
|
||||
bool MapGenLoadHeightmapImage(const utf8* path);
|
||||
void MapGenUnloadHeightmapImage();
|
||||
|
||||
Reference in New Issue
Block a user