diff --git a/data/language/en-GB.txt b/data/language/en-GB.txt index 3bacc3eb0c..cc572a5de9 100644 --- a/data/language/en-GB.txt +++ b/data/language/en-GB.txt @@ -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 # diff --git a/distribution/changelog.txt b/distribution/changelog.txt index c3c12a7dd0..61bcd94915 100644 --- a/distribution/changelog.txt +++ b/distribution/changelog.txt @@ -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. diff --git a/resources/g2/icons/map_gen_terrain_tab.png b/resources/g2/icons/map_gen_terrain_tab.png new file mode 100644 index 0000000000..5d4243e982 Binary files /dev/null and b/resources/g2/icons/map_gen_terrain_tab.png differ diff --git a/resources/g2/sprites.json b/resources/g2/sprites.json index fcfc613499..b99c597d75 100644 --- a/resources/g2/sprites.json +++ b/resources/g2/sprites.json @@ -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" }, diff --git a/src/openrct2-ui/UiStringIds.h b/src/openrct2-ui/UiStringIds.h index 84cd447ec6..93f1086549 100644 --- a/src/openrct2-ui/UiStringIds.h +++ b/src/openrct2-ui/UiStringIds.h @@ -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, diff --git a/src/openrct2-ui/interface/Widget.h b/src/openrct2-ui/interface/Widget.h index bc0f0e7777..8f1d82ab9e 100644 --- a/src/openrct2-ui/interface/Widget.h +++ b/src/openrct2-ui/interface/Widget.h @@ -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); } diff --git a/src/openrct2-ui/windows/EditorBottomToolbar.cpp b/src/openrct2-ui/windows/EditorBottomToolbar.cpp index 279f62dc42..c157a50e8d 100644 --- a/src/openrct2-ui/windows/EditorBottomToolbar.cpp +++ b/src/openrct2-ui/windows/EditorBottomToolbar.cpp @@ -199,6 +199,7 @@ namespace OpenRCT2::Ui::Windows else { ContextOpenWindow(WindowClass::Map); + ContextOpenWindow(WindowClass::Mapgen); } } diff --git a/src/openrct2-ui/windows/MapGen.cpp b/src/openrct2-ui/windows/MapGen.cpp index 3f6c6718d6..017fa07e48 100644 --- a/src/openrct2-ui/windows/MapGen.cpp +++ b/src/openrct2-ui/windows/MapGen.cpp @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include #include #include @@ -31,9 +33,9 @@ namespace OpenRCT2::Ui::Windows enum { WINDOW_MAPGEN_PAGE_BASE, - WINDOW_MAPGEN_PAGE_RANDOM, - WINDOW_MAPGEN_PAGE_SIMPLEX, - WINDOW_MAPGEN_PAGE_HEIGHTMAP, + WINDOW_MAPGEN_PAGE_TERRAIN, + WINDOW_MAPGEN_PAGE_WATER, + WINDOW_MAPGEN_PAGE_FORESTS, WINDOW_MAPGEN_PAGE_COUNT }; @@ -47,152 +49,132 @@ namespace OpenRCT2::Ui::Windows WIDX_TAB_2, WIDX_TAB_3, WIDX_TAB_4, + WIDX_MAP_GENERATE, TAB_BEGIN, - WIDX_MAP_GENERATE = TAB_BEGIN, - WIDX_MAP_SIZE_Y, + WIDX_MAP_SIZE_Y = TAB_BEGIN, WIDX_MAP_SIZE_Y_UP, WIDX_MAP_SIZE_Y_DOWN, WIDX_MAP_SIZE_LINK, WIDX_MAP_SIZE_X, WIDX_MAP_SIZE_X_UP, WIDX_MAP_SIZE_X_DOWN, - WIDX_BASE_HEIGHT, - WIDX_BASE_HEIGHT_UP, - WIDX_BASE_HEIGHT_DOWN, - WIDX_WATER_LEVEL, - WIDX_WATER_LEVEL_UP, - WIDX_WATER_LEVEL_DOWN, - WIDX_FLOOR_TEXTURE, - WIDX_WALL_TEXTURE, + WIDX_HEIGHTMAP_SOURCE, + WIDX_HEIGHTMAP_SOURCE_DROPDOWN, - WIDX_RANDOM_GENERATE = TAB_BEGIN, - WIDX_RANDOM_TERRAIN, - WIDX_RANDOM_PLACE_TREES, - - WIDX_SIMPLEX_GENERATE = TAB_BEGIN, - WIDX_SIMPLEX_LABEL, - WIDX_SIMPLEX_LOW, - WIDX_SIMPLEX_LOW_UP, - WIDX_SIMPLEX_LOW_DOWN, - WIDX_SIMPLEX_HIGH, - WIDX_SIMPLEX_HIGH_UP, - WIDX_SIMPLEX_HIGH_DOWN, + WIDX_SIMPLEX_GROUP, WIDX_SIMPLEX_BASE_FREQ, WIDX_SIMPLEX_BASE_FREQ_UP, WIDX_SIMPLEX_BASE_FREQ_DOWN, WIDX_SIMPLEX_OCTAVES, WIDX_SIMPLEX_OCTAVES_UP, WIDX_SIMPLEX_OCTAVES_DOWN, - WIDX_SIMPLEX_MAP_SIZE_Y, - WIDX_SIMPLEX_MAP_SIZE_Y_UP, - WIDX_SIMPLEX_MAP_SIZE_Y_DOWN, - WIDX_SIMPLEX_MAP_SIZE_LINK, - WIDX_SIMPLEX_MAP_SIZE_X, - WIDX_SIMPLEX_MAP_SIZE_X_UP, - WIDX_SIMPLEX_MAP_SIZE_X_DOWN, - WIDX_SIMPLEX_WATER_LEVEL, - WIDX_SIMPLEX_WATER_LEVEL_UP, - WIDX_SIMPLEX_WATER_LEVEL_DOWN, - WIDX_SIMPLEX_RANDOM_TERRAIN_CHECKBOX, - WIDX_SIMPLEX_FLOOR_TEXTURE, - WIDX_SIMPLEX_WALL_TEXTURE, - WIDX_SIMPLEX_PLACE_TREES_CHECKBOX, - WIDX_HEIGHTMAP_SELECT = TAB_BEGIN, + WIDX_HEIGHTMAP_GROUP, + WIDX_HEIGHTMAP_BROWSE, + WIDX_HEIGHTMAP_NORMALIZE, WIDX_HEIGHTMAP_SMOOTH_HEIGHTMAP, WIDX_HEIGHTMAP_STRENGTH, WIDX_HEIGHTMAP_STRENGTH_UP, WIDX_HEIGHTMAP_STRENGTH_DOWN, - WIDX_HEIGHTMAP_NORMALIZE, - WIDX_HEIGHTMAP_SMOOTH_TILES, - WIDX_HEIGHTMAP_LOW, + + WIDX_HEIGHTMAP_LOW = TAB_BEGIN, WIDX_HEIGHTMAP_LOW_UP, WIDX_HEIGHTMAP_LOW_DOWN, WIDX_HEIGHTMAP_HIGH, WIDX_HEIGHTMAP_HIGH_UP, WIDX_HEIGHTMAP_HIGH_DOWN, - WIDX_HEIGHTMAP_WATER_LEVEL, - WIDX_HEIGHTMAP_WATER_LEVEL_UP, - WIDX_HEIGHTMAP_WATER_LEVEL_DOWN, + WIDX_FLOOR_TEXTURE, + WIDX_WALL_TEXTURE, + WIDX_RANDOM_TERRAIN, + WIDX_HEIGHTMAP_SMOOTH_TILE_EDGES, + + WIDX_WATER_LEVEL = TAB_BEGIN, + WIDX_WATER_LEVEL_UP, + WIDX_WATER_LEVEL_DOWN, + WIDX_ADD_BEACHES, + + WIDX_FORESTS_PLACE_TREES = TAB_BEGIN, + WIDX_TREE_LAND_RATIO, + WIDX_TREE_LAND_RATIO_UP, + WIDX_TREE_LAND_RATIO_DOWN, + WIDX_TREE_ALTITUDE_MIN, + WIDX_TREE_ALTITUDE_MIN_UP, + WIDX_TREE_ALTITUDE_MIN_DOWN, + WIDX_TREE_ALTITUDE_MAX, + WIDX_TREE_ALTITUDE_MAX_UP, + WIDX_TREE_ALTITUDE_MAX_DOWN, }; #pragma region Widgets - static constexpr StringId WINDOW_TITLE = STR_MAPGEN_WINDOW_TITLE; - static constexpr int32_t WW = 250; - static constexpr int32_t WH = 273; + static constexpr int32_t WW = 300; + static constexpr int32_t WH = 220; -#define SHARED_WIDGETS \ - WINDOW_SHIM(WINDOW_TITLE, WW, WH), /* WIDX_BACKGROUND, WIDX_TITLE, WIDX_CLOSE */ \ - MakeWidget({ 0, 43 }, { WW, 229 }, WindowWidgetType::Resize, WindowColour::Secondary), /* WIDX_PAGE_BACKGROUND */ \ +#define SHARED_WIDGETS(PAGE_TITLE) \ + WINDOW_SHIM(PAGE_TITLE, WW, WH), /* WIDX_BACKGROUND, WIDX_TITLE, WIDX_CLOSE */ \ + MakeWidget({ 0, 43 }, { WW, 177 }, WindowWidgetType::Resize, WindowColour::Secondary), /* WIDX_PAGE_BACKGROUND */ \ MakeTab({ 3, 17 }), /* WIDX_TAB_1 */ \ MakeTab({ 34, 17 }), /* WIDX_TAB_2 */ \ MakeTab({ 65, 17 }), /* WIDX_TAB_3 */ \ - MakeTab({ 96, 17 }) /* WIDX_TAB_4 */ + MakeTab({ 96, 17 }), /* WIDX_TAB_4 */ \ + MakeWidget({ 185, 200 }, { 109, 14 }, WindowWidgetType::Button, WindowColour::Secondary, STR_MAPGEN_ACTION_GENERATE) // clang-format off - static Widget MapWidgets[] = { - SHARED_WIDGETS, - MakeWidget ({155, 255}, {90, 14}, WindowWidgetType::Button, WindowColour::Secondary, STR_MAPGEN_ACTION_GENERATE ), - MakeSpinnerWidgets({104, 52}, {50, 12}, WindowWidgetType::Spinner, WindowColour::Secondary, STR_COMMA16 ), // NB: 3 widgets - MakeWidget ({155, 52}, {21, 12}, WindowWidgetType::FlatBtn, WindowColour::Secondary, ImageId(SPR_G2_LINK_CHAIN), STR_MAINTAIN_SQUARE_MAP_TOOLTIP), - MakeSpinnerWidgets({177, 52}, {50, 12}, WindowWidgetType::Spinner, WindowColour::Secondary, STR_POP16_COMMA16 ), // NB: 3 widgets - MakeSpinnerWidgets({104, 70}, {95, 12}, WindowWidgetType::Spinner, WindowColour::Secondary ), // NB: 3 widgets - MakeSpinnerWidgets({104, 88}, {95, 12}, WindowWidgetType::Spinner, WindowColour::Secondary ), // NB: 3 widgets - MakeWidget ({104, 106}, {47, 36}, WindowWidgetType::FlatBtn, WindowColour::Secondary, 0xFFFFFFFF, STR_CHANGE_BASE_LAND_TIP ), - MakeWidget ({151, 106}, {47, 36}, WindowWidgetType::FlatBtn, WindowColour::Secondary, 0xFFFFFFFF, STR_CHANGE_VERTICAL_LAND_TIP ), + static Widget BaseWidgets[] = { + SHARED_WIDGETS(STR_MAPGEN_CAPTION_GENERATOR), + MakeSpinnerWidgets ({165, 52}, { 50, 12}, WindowWidgetType::Spinner, WindowColour::Secondary, STR_COMMA16 ), // NB: 3 widgets + MakeWidget ({216, 52}, { 21, 12}, WindowWidgetType::FlatBtn, WindowColour::Secondary, ImageId(SPR_G2_LINK_CHAIN), STR_MAINTAIN_SQUARE_MAP_TOOLTIP), + MakeSpinnerWidgets ({238, 52}, { 50, 12}, WindowWidgetType::Spinner, WindowColour::Secondary, STR_POP16_COMMA16 ), // NB: 3 widgets + MakeDropdownWidgets({155, 70}, {133, 14}, WindowWidgetType::DropdownMenu, WindowColour::Secondary, STR_HEIGHTMAP_FLATLAND ), + + MakeWidget ({ 5, 90}, {290, 55}, WindowWidgetType::Groupbox, WindowColour::Secondary, STR_MAPGEN_SIMPLEX_NOISE), // WIDX_SIMPLEX_GROUP + MakeSpinnerWidgets({179, 107}, {109, 12}, WindowWidgetType::Spinner, WindowColour::Secondary ), // WIDX_SIMPLEX_BASE_FREQ{,_UP,_DOWN} + MakeSpinnerWidgets({179, 125}, {109, 12}, WindowWidgetType::Spinner, WindowColour::Secondary ), // WIDX_SIMPLEX_OCTAVES{,_UP,_DOWN} + + MakeWidget ({ 5, 90}, {290, 86}, WindowWidgetType::Groupbox, WindowColour::Secondary, STR_MAPGEN_SELECT_HEIGHTMAP), // WIDX_HEIGHTMAP_GROUP + MakeWidget ({223, 107}, { 65, 14}, WindowWidgetType::Button, WindowColour::Secondary, STR_BROWSE ), // WIDX_HEIGHTMAP_BROWSE + MakeWidget ({ 10, 125}, {150, 12}, WindowWidgetType::Checkbox, WindowColour::Secondary, STR_MAPGEN_NORMALIZE ), // WIDX_HEIGHTMAP_NORMALIZE + MakeWidget ({ 10, 141}, {150, 12}, WindowWidgetType::Checkbox, WindowColour::Secondary, STR_MAPGEN_SMOOTH_HEIGHTMAP), // WIDX_HEIGHTMAP_SMOOTH_HEIGHTMAP + MakeSpinnerWidgets({179, 157}, {109, 12}, WindowWidgetType::Spinner, WindowColour::Secondary ), // WIDX_HEIGHTMAP_STRENGTH{,_UP,_DOWN} kWidgetsEnd, }; - static Widget RandomWidgets[] = { - SHARED_WIDGETS, - MakeWidget({155, 255}, { 90, 14}, WindowWidgetType::Button, WindowColour::Secondary, STR_MAPGEN_ACTION_GENERATE ), - MakeWidget({ 4, 52}, {195, 12}, WindowWidgetType::Checkbox, WindowColour::Secondary, STR_MAPGEN_OPTION_RANDOM_TERRAIN), - MakeWidget({ 4, 70}, {195, 12}, WindowWidgetType::Checkbox, WindowColour::Secondary, STR_MAPGEN_OPTION_PLACE_TREES ), + static Widget TerrainWidgets[] = { + SHARED_WIDGETS(STR_MAPGEN_CAPTION_TERRAIN), + MakeSpinnerWidgets({179, 52}, {109, 12}, WindowWidgetType::Spinner, WindowColour::Secondary ), // WIDX_HEIGHTMAP_LOW{,_UP,_DOWN} + MakeSpinnerWidgets({179, 70}, {109, 12}, WindowWidgetType::Spinner, WindowColour::Secondary ), // WIDX_HEIGHTMAP_HIGH{,_UP,_DOWN} + MakeWidget ({179, 88}, { 47, 36}, WindowWidgetType::FlatBtn, WindowColour::Secondary, 0xFFFFFFFF, STR_CHANGE_BASE_LAND_TIP ), + MakeWidget ({236, 88}, { 47, 36}, WindowWidgetType::FlatBtn, WindowColour::Secondary, 0xFFFFFFFF, STR_CHANGE_VERTICAL_LAND_TIP), + MakeWidget ({ 10, 106}, {150, 12}, WindowWidgetType::Checkbox, WindowColour::Secondary, STR_MAPGEN_OPTION_RANDOM_TERRAIN ), + MakeWidget ({ 10, 122}, {150, 12}, WindowWidgetType::Checkbox, WindowColour::Secondary, STR_MAPGEN_SMOOTH_TILE), // WIDX_HEIGHTMAP_SMOOTH_TILE_EDGES kWidgetsEnd, }; - static Widget SimplexWidgets[] = { - SHARED_WIDGETS, - MakeWidget ({155, 255}, { 90, 14}, WindowWidgetType::Button, WindowColour::Secondary, STR_MAPGEN_ACTION_GENERATE ), // WIDX_SIMPLEX_GENERATE - MakeWidget ({ 4, 52}, {195, 12}, WindowWidgetType::LabelCentred, WindowColour::Secondary, STR_MAPGEN_SIMPLEX_NOISE ), // WIDX_SIMPLEX_LABEL - MakeSpinnerWidgets({104, 70}, { 95, 12}, WindowWidgetType::Spinner, WindowColour::Secondary ), // WIDX_SIMPLEX_LOW{,_UP,_DOWN} - MakeSpinnerWidgets({104, 88}, { 95, 12}, WindowWidgetType::Spinner, WindowColour::Secondary ), // WIDX_SIMPLEX_HIGH{,_UP,_DOWN} - MakeSpinnerWidgets({104, 106}, { 95, 12}, WindowWidgetType::Spinner, WindowColour::Secondary ), // WIDX_SIMPLEX_BASE_FREQ{,_UP,_DOWN} - MakeSpinnerWidgets({104, 124}, { 95, 12}, WindowWidgetType::Spinner, WindowColour::Secondary ), // WIDX_SIMPLEX_OCTAVES{,_UP,_DOWN} - MakeSpinnerWidgets({104, 148}, { 50, 12}, WindowWidgetType::Spinner, WindowColour::Secondary, STR_COMMA16 ), // WIDX_SIMPLEX_MAP_SIZE_Y{,_UP,_DOWN} - MakeWidget ({155, 148}, { 21, 12}, WindowWidgetType::FlatBtn, WindowColour::Secondary, ImageId(SPR_G2_LINK_CHAIN), STR_MAINTAIN_SQUARE_MAP_TOOLTIP), // WIDX_SIMPLEX_MAP_SIZE_LINK - MakeSpinnerWidgets({177, 148}, { 50, 12}, WindowWidgetType::Spinner, WindowColour::Secondary, STR_POP16_COMMA16 ), // WIDX_SIMPLEX_MAP_SIZE_X{,_UP,_DOWN} - MakeSpinnerWidgets({104, 166}, { 95, 12}, WindowWidgetType::Spinner, WindowColour::Secondary ), // WIDX_SIMPLEX_WATER_LEVEL{,_UP,_DOWN} - MakeWidget ({104, 190}, { 95, 12}, WindowWidgetType::Checkbox, WindowColour::Secondary, STR_MAPGEN_OPTION_RANDOM_TERRAIN ), // WIDX_SIMPLEX_RANDOM_TERRAIN_CHECKBOX - MakeWidget ({102, 202}, { 47, 36}, WindowWidgetType::FlatBtn, WindowColour::Secondary, 0xFFFFFFFF, STR_CHANGE_BASE_LAND_TIP ), // WIDX_SIMPLEX_FLOOR_TEXTURE - MakeWidget ({150, 202}, { 47, 36}, WindowWidgetType::FlatBtn, WindowColour::Secondary, 0xFFFFFFFF, STR_CHANGE_VERTICAL_LAND_TIP ), // WIDX_SIMPLEX_WALL_TEXTURE - MakeWidget ({104, 239}, { 95, 12}, WindowWidgetType::Checkbox, WindowColour::Secondary ), // WIDX_SIMPLEX_PLACE_TREES_CHECKBOX + static Widget WaterWidgets[] = { + SHARED_WIDGETS(STR_MAPGEN_CAPTION_WATER), + MakeSpinnerWidgets({179, 52}, {109, 12}, WindowWidgetType::Spinner, WindowColour::Secondary ), // NB: 3 widgets + MakeWidget ({ 10, 70}, {195, 12}, WindowWidgetType::Checkbox, WindowColour::Secondary, STR_BEACHES_WATER_BODIES), kWidgetsEnd, }; - static Widget HeightmapWidgets[] = { - SHARED_WIDGETS, - MakeWidget ({ 95, 255}, {150, 14}, WindowWidgetType::Button, WindowColour::Secondary, STR_MAPGEN_SELECT_HEIGHTMAP), // WIDX_HEIGHTMAP_SELECT - MakeWidget ({ 4, 52}, {100, 12}, WindowWidgetType::Checkbox, WindowColour::Secondary, STR_MAPGEN_SMOOTH_HEIGHTMAP), // WIDX_HEIGHTMAP_SMOOTH_HEIGHTMAP - MakeSpinnerWidgets({104, 70}, { 95, 12}, WindowWidgetType::Spinner, WindowColour::Secondary ), // WIDX_HEIGHTMAP_STRENGTH{,_UP,_DOWN} - MakeWidget ({ 4, 88}, {100, 12}, WindowWidgetType::Checkbox, WindowColour::Secondary, STR_MAPGEN_NORMALIZE ), // WIDX_HEIGHTMAP_NORMALIZE - MakeWidget ({ 4, 106}, {100, 12}, WindowWidgetType::Checkbox, WindowColour::Secondary, STR_MAPGEN_SMOOTH_TILE ), // WIDX_HEIGHTMAP_SMOOTH_TILES - MakeSpinnerWidgets({104, 124}, { 95, 12}, WindowWidgetType::Spinner, WindowColour::Secondary ), // WIDX_HEIGHTMAP_LOW{,_UP,_DOWN} - MakeSpinnerWidgets({104, 142}, { 95, 12}, WindowWidgetType::Spinner, WindowColour::Secondary ), // WIDX_HEIGHTMAP_HIGH{,_UP,_DOWN} - MakeSpinnerWidgets({104, 160}, { 95, 12}, WindowWidgetType::Spinner, WindowColour::Secondary ), // WIDX_HEIGHTMAP_WATER_LEVEL{,_UP,_DOWN} + static Widget ForestsWidgets[] = { + SHARED_WIDGETS(STR_MAPGEN_CAPTION_FORESTS), + MakeWidget ({ 10, 52}, {255, 12}, WindowWidgetType::Checkbox, WindowColour::Secondary, STR_MAPGEN_OPTION_PLACE_TREES), + MakeSpinnerWidgets({179, 70}, {109, 12}, WindowWidgetType::Spinner, WindowColour::Secondary), // WIDX_TREE_LAND_RATIO{,_UP,_DOWN} + MakeSpinnerWidgets({179, 88}, {109, 12}, WindowWidgetType::Spinner, WindowColour::Secondary), // WIDX_TREE_ALTITUDE_MIN{,_UP,_DOWN} + MakeSpinnerWidgets({179, 106}, {109, 12}, WindowWidgetType::Spinner, WindowColour::Secondary), // WIDX_TREE_ALTITUDE_MAX{,_UP,_DOWN} kWidgetsEnd, }; - // clang-format on static Widget* PageWidgets[WINDOW_MAPGEN_PAGE_COUNT] = { - MapWidgets, - RandomWidgets, - SimplexWidgets, - HeightmapWidgets, + BaseWidgets, + TerrainWidgets, + WaterWidgets, + ForestsWidgets, }; + // clang-format on #pragma endregion @@ -200,27 +182,17 @@ namespace OpenRCT2::Ui::Windows // clang-format off static uint64_t PageDisabledWidgets[WINDOW_MAPGEN_PAGE_COUNT] = { - 0, - - 0, - - 0, - (1uLL << WIDX_HEIGHTMAP_SMOOTH_HEIGHTMAP) | (1uLL << WIDX_HEIGHTMAP_STRENGTH) | (1uLL << WIDX_HEIGHTMAP_STRENGTH_UP) | (1uLL << WIDX_HEIGHTMAP_STRENGTH_DOWN) | - (1uLL << WIDX_HEIGHTMAP_NORMALIZE) | - (1uLL << WIDX_HEIGHTMAP_SMOOTH_TILES) | - (1uLL << WIDX_HEIGHTMAP_HIGH) | - (1uLL << WIDX_HEIGHTMAP_HIGH_UP) | - (1uLL << WIDX_HEIGHTMAP_HIGH_DOWN) | - (1uLL << WIDX_HEIGHTMAP_LOW) | - (1uLL << WIDX_HEIGHTMAP_LOW_UP) | - (1uLL << WIDX_HEIGHTMAP_LOW_DOWN) | - (1uLL << WIDX_HEIGHTMAP_WATER_LEVEL) | - (1uLL << WIDX_HEIGHTMAP_WATER_LEVEL_UP) | - (1uLL << WIDX_HEIGHTMAP_WATER_LEVEL_DOWN) + (1uLL << WIDX_HEIGHTMAP_NORMALIZE), + + 0, + + 0, + + 0 }; static uint64_t HoldDownWidgets[WINDOW_MAPGEN_PAGE_COUNT] = { @@ -228,74 +200,60 @@ namespace OpenRCT2::Ui::Windows (1uLL << WIDX_MAP_SIZE_Y_DOWN) | (1uLL << WIDX_MAP_SIZE_X_UP) | (1uLL << WIDX_MAP_SIZE_X_DOWN) | - (1uLL << WIDX_BASE_HEIGHT_UP) | - (1uLL << WIDX_BASE_HEIGHT_DOWN) | - (1uLL << WIDX_WATER_LEVEL_UP) | - (1uLL << WIDX_WATER_LEVEL_DOWN), - - 0, - - (1uLL << WIDX_SIMPLEX_LOW_UP) | - (1uLL << WIDX_SIMPLEX_LOW_DOWN) | - (1uLL << WIDX_SIMPLEX_HIGH_UP) | - (1uLL << WIDX_SIMPLEX_HIGH_DOWN) | (1uLL << WIDX_SIMPLEX_BASE_FREQ_UP) | (1uLL << WIDX_SIMPLEX_BASE_FREQ_DOWN) | (1uLL << WIDX_SIMPLEX_OCTAVES_UP) | (1uLL << WIDX_SIMPLEX_OCTAVES_DOWN) | - (1uLL << WIDX_SIMPLEX_MAP_SIZE_Y_UP) | - (1uLL << WIDX_SIMPLEX_MAP_SIZE_Y_DOWN) | - (1uLL << WIDX_SIMPLEX_MAP_SIZE_X_UP) | - (1uLL << WIDX_SIMPLEX_MAP_SIZE_X_DOWN) | - (1uLL << WIDX_SIMPLEX_WATER_LEVEL_UP) | - (1uLL << WIDX_SIMPLEX_WATER_LEVEL_DOWN), - (1uLL << WIDX_HEIGHTMAP_STRENGTH_UP) | - (1uLL << WIDX_HEIGHTMAP_STRENGTH_DOWN) | + (1uLL << WIDX_HEIGHTMAP_STRENGTH_DOWN), + (1uLL << WIDX_HEIGHTMAP_LOW_UP) | (1uLL << WIDX_HEIGHTMAP_LOW_DOWN) | (1uLL << WIDX_HEIGHTMAP_HIGH_UP) | - (1uLL << WIDX_HEIGHTMAP_HIGH_DOWN) | - (1uLL << WIDX_HEIGHTMAP_WATER_LEVEL_UP) | - (1uLL << WIDX_HEIGHTMAP_WATER_LEVEL_DOWN) + (1uLL << WIDX_HEIGHTMAP_HIGH_DOWN), + + (1uLL << WIDX_WATER_LEVEL_UP) | + (1uLL << WIDX_WATER_LEVEL_DOWN), + + (1uLL << WIDX_TREE_LAND_RATIO_UP) | + (1uLL << WIDX_TREE_LAND_RATIO_DOWN) | + (1uLL << WIDX_TREE_ALTITUDE_MIN_UP) | + (1uLL << WIDX_TREE_ALTITUDE_MIN_DOWN) | + (1uLL << WIDX_TREE_ALTITUDE_MAX_UP) | + (1uLL << WIDX_TREE_ALTITUDE_MAX_DOWN), }; static uint64_t PressedWidgets[WINDOW_MAPGEN_PAGE_COUNT] = { 0, + (1uLL << WIDX_HEIGHTMAP_SMOOTH_TILE_EDGES), 0, 0, - (1uLL << WIDX_HEIGHTMAP_SMOOTH_TILES) }; // clang-format on #pragma endregion + // clang-format off static constexpr int32_t TabAnimationDivisor[WINDOW_MAPGEN_PAGE_COUNT] = { - 1, + 2, 1, 1, 1, }; static constexpr int32_t TabAnimationFrames[WINDOW_MAPGEN_PAGE_COUNT] = { - 1, + 4, 1, 1, 1, }; static constexpr int32_t TabAnimationLoops[WINDOW_MAPGEN_PAGE_COUNT] = { 16, - 16, - 16, - 0, + 1, + 1, + 1, }; // clang-format on - constexpr int32_t BASESIZE_MIN = 0; - constexpr int32_t BASESIZE_MAX = 60; - constexpr int32_t WATERLEVEL_MIN = 0; - constexpr int32_t WATERLEVEL_MAX = 54; - constexpr int32_t MAX_SMOOTH_ITERATIONS = 20; - enum class ResizeDirection { Both, @@ -308,28 +266,12 @@ namespace OpenRCT2::Ui::Windows class MapGenWindow final : public Window { private: - TileCoordsXY _mapSize{ 150, 150 }; ResizeDirection _resizeDirection{ ResizeDirection::Both }; bool _mapWidthAndHeightLinked{ true }; - int32_t _baseHeight = 12; - int32_t _waterLevel = 6; - int32_t _floorTexture = 0; - int32_t _wallTexture = 0; + MapGenSettings _settings{}; bool _randomTerrain = true; - int32_t _placeTrees = 1; - - int32_t _simplex_low = 6; - int32_t _simplex_high = 10; - int32_t _simplex_base_freq = 60; - int32_t _simplex_octaves = 4; - bool _heightmapLoaded = false; - bool _heightmapSmoothMap = false; - int32_t _heightmapSmoothStrength = 1; - bool _heightmapNormalize = false; - bool _heightmapSmoothTiles = true; - int32_t _heightmapLow = 2; - int32_t _heightmapHigh = 70; + std::string _heightmapFilename{}; void SetPage(int32_t newPage) { @@ -342,34 +284,13 @@ namespace OpenRCT2::Ui::Windows disabled_widgets = PageDisabledWidgets[newPage]; pressed_widgets = PressedWidgets[newPage]; - // Enable heightmap widgets if one is loaded - if (newPage == WINDOW_MAPGEN_PAGE_HEIGHTMAP && _heightmapLoaded) - { - SetWidgetEnabled(WIDX_HEIGHTMAP_SMOOTH_HEIGHTMAP, true); - SetWidgetEnabled(WIDX_HEIGHTMAP_STRENGTH, _heightmapSmoothMap); - SetWidgetEnabled(WIDX_HEIGHTMAP_STRENGTH_UP, _heightmapSmoothMap); - SetWidgetEnabled(WIDX_HEIGHTMAP_STRENGTH_DOWN, _heightmapSmoothMap); - SetWidgetEnabled(WIDX_HEIGHTMAP_NORMALIZE, true); - SetWidgetEnabled(WIDX_HEIGHTMAP_SMOOTH_TILES, true); - SetWidgetEnabled(WIDX_HEIGHTMAP_HIGH, true); - SetWidgetEnabled(WIDX_HEIGHTMAP_HIGH_UP, true); - SetWidgetEnabled(WIDX_HEIGHTMAP_HIGH_DOWN, true); - SetWidgetEnabled(WIDX_HEIGHTMAP_LOW, true); - SetWidgetEnabled(WIDX_HEIGHTMAP_LOW_UP, true); - SetWidgetEnabled(WIDX_HEIGHTMAP_LOW_DOWN, true); - SetWidgetEnabled(WIDX_HEIGHTMAP_WATER_LEVEL, true); - SetWidgetEnabled(WIDX_HEIGHTMAP_WATER_LEVEL_UP, true); - SetWidgetEnabled(WIDX_HEIGHTMAP_WATER_LEVEL_DOWN, true); - } - InitScrollWidgets(); Invalidate(); } void SetPressedTab() { - int32_t i; - for (i = 0; i < WINDOW_MAPGEN_PAGE_COUNT; i++) + for (auto i = 0; i < WINDOW_MAPGEN_PAGE_COUNT; i++) pressed_widgets &= ~(1 << (WIDX_TAB_1 + i)); pressed_widgets |= 1LL << (WIDX_TAB_1 + page); } @@ -394,10 +315,10 @@ namespace OpenRCT2::Ui::Windows void DrawTabImages(DrawPixelInfo& dpi) { - DrawTabImage(dpi, WINDOW_MAPGEN_PAGE_BASE, SPR_G2_TAB_LAND); - DrawTabImage(dpi, WINDOW_MAPGEN_PAGE_RANDOM, SPR_G2_TAB_TREE); - DrawTabImage(dpi, WINDOW_MAPGEN_PAGE_SIMPLEX, SPR_G2_TAB_PENCIL); - DrawTabImage(dpi, WINDOW_MAPGEN_PAGE_HEIGHTMAP, SPR_TAB_GRAPH_0); + DrawTabImage(dpi, WINDOW_MAPGEN_PAGE_BASE, SPR_TAB_GEARS_0); + DrawTabImage(dpi, WINDOW_MAPGEN_PAGE_TERRAIN, SPR_G2_MAP_GEN_TERRAIN_TAB); + DrawTabImage(dpi, WINDOW_MAPGEN_PAGE_WATER, SPR_TAB_WATER); + DrawTabImage(dpi, WINDOW_MAPGEN_PAGE_FORESTS, SPR_TAB_SCENERY_TREES); } void ChangeMapSize(int32_t sizeOffset) @@ -406,13 +327,16 @@ namespace OpenRCT2::Ui::Windows _resizeDirection = ResizeDirection::Both; if (_resizeDirection != ResizeDirection::X) - _mapSize.y = std::clamp( - _mapSize.y + sizeOffset, static_cast(kMinimumMapSizeTechnical), - static_cast(kMaximumMapSizeTechnical)); + { + _settings.mapSize.y = std::clamp( + _settings.mapSize.y + sizeOffset, kMinimumMapSizeTechnical, kMaximumMapSizeTechnical); + } + if (_resizeDirection != ResizeDirection::Y) - _mapSize.x = std::clamp( - _mapSize.x + sizeOffset, static_cast(kMinimumMapSizeTechnical), - static_cast(kMaximumMapSizeTechnical)); + { + _settings.mapSize.x = std::clamp( + _settings.mapSize.x + sizeOffset, kMinimumMapSizeTechnical, kMaximumMapSizeTechnical); + } } void InputMapSize(WidgetIndex callingWidget, int32_t currentValue) @@ -440,59 +364,63 @@ namespace OpenRCT2::Ui::Windows case WIDX_TAB_4: SetPage(widgetIndex - WIDX_TAB_1); break; + case WIDX_MAP_GENERATE: + GenerateMap(); + break; } } + void GenerateMap() + { + if (_settings.algorithm == MapGenAlgorithm::heightmapImage && !_heightmapLoaded) + return; + + MapGenSettings mapgenSettings = _settings; + if (_randomTerrain) + { + mapgenSettings.landTexture = -1; + mapgenSettings.edgeTexture = -1; + } + + MapGenGenerate(&mapgenSettings); + GfxInvalidateScreen(); + } + #pragma region Base page void BaseMouseUp(WidgetIndex widgetIndex) { SharedMouseUp(widgetIndex); - MapGenSettings mapgenSettings; - Formatter ft; + if (_settings.algorithm == MapGenAlgorithm::simplexNoise) + SimplexMouseUp(widgetIndex); + else if (_settings.algorithm == MapGenAlgorithm::heightmapImage) + HeightmapMouseUp(widgetIndex); + switch (widgetIndex) { - case WIDX_MAP_GENERATE: - mapgenSettings.mapSize = _mapSize; - mapgenSettings.height = _baseHeight + 2; - mapgenSettings.water_level = _waterLevel + 2; - mapgenSettings.floor = _floorTexture; - mapgenSettings.wall = _wallTexture; - - MapGenGenerateBlank(&mapgenSettings); - GfxInvalidateScreen(); - break; case WIDX_MAP_SIZE_Y: _resizeDirection = ResizeDirection::Y; - InputMapSize(WIDX_MAP_SIZE_Y, _mapSize.y); + InputMapSize(WIDX_MAP_SIZE_Y, _settings.mapSize.y); break; case WIDX_MAP_SIZE_X: _resizeDirection = ResizeDirection::X; - InputMapSize(WIDX_MAP_SIZE_X, _mapSize.x); + InputMapSize(WIDX_MAP_SIZE_X, _settings.mapSize.x); break; case WIDX_MAP_SIZE_LINK: _mapWidthAndHeightLinked = !_mapWidthAndHeightLinked; break; - case WIDX_BASE_HEIGHT: - ft.Add((BASESIZE_MIN - 12) / 2); - ft.Add((BASESIZE_MAX - 12) / 2); - WindowTextInputOpen( - this, WIDX_BASE_HEIGHT, STR_BASE_HEIGHT, STR_ENTER_BASE_HEIGHT, ft, STR_FORMAT_INTEGER, - (_baseHeight - 12) / 2, 3); - break; - case WIDX_WATER_LEVEL: - ft.Add((WATERLEVEL_MIN - 12) / 2); - ft.Add((WATERLEVEL_MAX - 12) / 2); - WindowTextInputOpen( - this, WIDX_WATER_LEVEL, STR_WATER_LEVEL, STR_ENTER_WATER_LEVEL, ft, STR_FORMAT_INTEGER, - (_waterLevel - 12) / 2, 3); - break; } } void BaseMouseDown(WidgetIndex widgetIndex, Widget* widget) { + if (_settings.algorithm == MapGenAlgorithm::simplexNoise) + SimplexMouseDown(widgetIndex, widget); + + else if (_settings.algorithm == MapGenAlgorithm::heightmapImage) + HeightmapMouseDown(widgetIndex, widget); + switch (widgetIndex) { case WIDX_MAP_SIZE_Y_UP: @@ -515,71 +443,26 @@ namespace OpenRCT2::Ui::Windows ChangeMapSize(-1); Invalidate(); break; - case WIDX_BASE_HEIGHT_UP: - _baseHeight = std::min(_baseHeight + 2, BASESIZE_MAX); - Invalidate(); - break; - case WIDX_BASE_HEIGHT_DOWN: - _baseHeight = std::max(_baseHeight - 2, BASESIZE_MIN); - Invalidate(); - break; - case WIDX_WATER_LEVEL_UP: - _waterLevel = std::min(_waterLevel + 2, WATERLEVEL_MAX); - Invalidate(); - break; - case WIDX_WATER_LEVEL_DOWN: - _waterLevel = std::max(_waterLevel - 2, WATERLEVEL_MIN); - Invalidate(); - break; - case WIDX_FLOOR_TEXTURE: - LandTool::ShowSurfaceStyleDropdown(this, widget, _floorTexture); - break; - case WIDX_WALL_TEXTURE: - LandTool::ShowEdgeStyleDropdown(this, widget, _wallTexture); - break; - } - } + case WIDX_HEIGHTMAP_SOURCE_DROPDOWN: + { + using namespace Dropdown; - void BaseDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) - { - int32_t type; + constexpr ItemExt items[] = { + ToggleOption(0, STR_HEIGHTMAP_FLATLAND), + ToggleOption(1, STR_HEIGHTMAP_SIMPLEX_NOISE), + ToggleOption(2, STR_HEIGHTMAP_FILE), + }; - switch (widgetIndex) - { - case WIDX_FLOOR_TEXTURE: - if (dropdownIndex == -1) - dropdownIndex = gDropdownHighlightedIndex; + SetItems(items); - type = (dropdownIndex == -1) ? _floorTexture : dropdownIndex; + Widget* ddWidget = &widgets[widgetIndex - 1]; + WindowDropdownShowTextCustomWidth( + { windowPos.x + ddWidget->left, windowPos.y + ddWidget->top }, ddWidget->height() + 1, colours[1], 0, + Dropdown::Flag::StayOpen, std::size(items), ddWidget->width() - 2); - if (gLandToolTerrainSurface == type) - { - gLandToolTerrainSurface = OBJECT_ENTRY_INDEX_NULL; - } - else - { - gLandToolTerrainSurface = type; - _floorTexture = type; - } - Invalidate(); - break; - case WIDX_WALL_TEXTURE: - if (dropdownIndex == -1) - dropdownIndex = gDropdownHighlightedIndex; - - type = (dropdownIndex == -1) ? _wallTexture : dropdownIndex; - - if (gLandToolTerrainEdge == type) - { - gLandToolTerrainEdge = OBJECT_ENTRY_INDEX_NULL; - } - else - { - gLandToolTerrainEdge = type; - _wallTexture = type; - } - Invalidate(); + SetChecked(EnumValue(_settings.algorithm), true); break; + } } } @@ -591,37 +474,37 @@ namespace OpenRCT2::Ui::Windows InvalidateWidget(WIDX_TAB_1); } - void TextInput(WidgetIndex widgetIndex, std::string_view text) + void BaseDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) { - int32_t value; - char* end; - - const auto strText = u8string(text); - value = strtol(strText.c_str(), &end, 10); - - if (*end != '\0') - { + if (dropdownIndex == -1) return; + + switch (widgetIndex) + { + case WIDX_HEIGHTMAP_SOURCE_DROPDOWN: + _settings.algorithm = MapGenAlgorithm(dropdownIndex); + Invalidate(); + break; } + } + + void BaseTextInput(WidgetIndex widgetIndex, int32_t value) + { + if (_settings.algorithm == MapGenAlgorithm::simplexNoise) + SimplexTextInput(widgetIndex, value); + else if (_settings.algorithm == MapGenAlgorithm::heightmapImage) + HeightmapTextInput(widgetIndex, value); switch (widgetIndex) { case WIDX_MAP_SIZE_Y: case WIDX_MAP_SIZE_X: - case WIDX_SIMPLEX_MAP_SIZE_Y: - case WIDX_SIMPLEX_MAP_SIZE_X: // The practical size is 2 lower than the technical size value += 2; if (_resizeDirection == ResizeDirection::Y || _mapWidthAndHeightLinked) - _mapSize.y = value; + _settings.mapSize.y = value; if (_resizeDirection == ResizeDirection::X || _mapWidthAndHeightLinked) - _mapSize.x = value; - break; - case WIDX_BASE_HEIGHT: - _baseHeight = std::clamp((value * 2) + 12, BASESIZE_MIN, BASESIZE_MAX); - break; - case WIDX_WATER_LEVEL: - _waterLevel = std::clamp((value * 2) + 12, WATERLEVEL_MIN, WATERLEVEL_MAX); + _settings.mapSize.x = value; break; } @@ -638,14 +521,651 @@ namespace OpenRCT2::Ui::Windows // Only allow linking the map size when X and Y are the same SetWidgetPressed(WIDX_MAP_SIZE_LINK, _mapWidthAndHeightLinked); - SetWidgetDisabled(WIDX_MAP_SIZE_LINK, _mapSize.x != _mapSize.y); + SetWidgetDisabled(WIDX_MAP_SIZE_LINK, _settings.mapSize.x != _settings.mapSize.y); + + bool isHeightMapImage = _settings.algorithm == MapGenAlgorithm::heightmapImage; + SetWidgetDisabled(WIDX_MAP_SIZE_Y, isHeightMapImage); + SetWidgetDisabled(WIDX_MAP_SIZE_Y_UP, isHeightMapImage); + SetWidgetDisabled(WIDX_MAP_SIZE_Y_DOWN, isHeightMapImage); + SetWidgetDisabled(WIDX_MAP_SIZE_LINK, isHeightMapImage); + SetWidgetDisabled(WIDX_MAP_SIZE_X, isHeightMapImage); + SetWidgetDisabled(WIDX_MAP_SIZE_X_UP, isHeightMapImage); + SetWidgetDisabled(WIDX_MAP_SIZE_X_DOWN, isHeightMapImage); + + // Enable heightmap widgets if one is loaded + if (isHeightMapImage) + { + SetWidgetEnabled(WIDX_HEIGHTMAP_NORMALIZE, _heightmapLoaded); + SetWidgetEnabled(WIDX_HEIGHTMAP_SMOOTH_HEIGHTMAP, _heightmapLoaded); + SetWidgetEnabled(WIDX_HEIGHTMAP_STRENGTH, _heightmapLoaded && _settings.smooth_height_map); + SetWidgetEnabled(WIDX_HEIGHTMAP_STRENGTH_UP, _heightmapLoaded && _settings.smooth_height_map); + SetWidgetEnabled(WIDX_HEIGHTMAP_STRENGTH_DOWN, _heightmapLoaded && _settings.smooth_height_map); + } SetPressedTab(); // Push width (Y) and height (X) to the common formatter arguments for the map size spinners to use auto ft = Formatter::Common(); - ft.Add(_mapSize.y - 2); - ft.Add(_mapSize.x - 2); + ft.Add(_settings.mapSize.y - 2); + ft.Add(_settings.mapSize.x - 2); + + auto& sourceWidget = widgets[WIDX_HEIGHTMAP_SOURCE]; + switch (_settings.algorithm) + { + case MapGenAlgorithm::blank: + sourceWidget.text = STR_HEIGHTMAP_FLATLAND; + ToggleSimplexWidgets(false); + ToggleHeightmapWidgets(false); + break; + + case MapGenAlgorithm::simplexNoise: + sourceWidget.text = STR_HEIGHTMAP_SIMPLEX_NOISE; + ToggleSimplexWidgets(true); + ToggleHeightmapWidgets(false); + break; + + case MapGenAlgorithm::heightmapImage: + sourceWidget.text = STR_HEIGHTMAP_FILE; + ToggleSimplexWidgets(false); + ToggleHeightmapWidgets(true); + HeightmapPrepareDraw(); + break; + } + } + + void ToggleSimplexWidgets(bool state) + { + // clang-format off + BaseWidgets[WIDX_SIMPLEX_GROUP].type = state ? WindowWidgetType::Groupbox : WindowWidgetType::Empty; + BaseWidgets[WIDX_SIMPLEX_BASE_FREQ].type = state ? WindowWidgetType::Spinner : WindowWidgetType::Empty; + BaseWidgets[WIDX_SIMPLEX_BASE_FREQ_UP].type = state ? WindowWidgetType::Button : WindowWidgetType::Empty; + BaseWidgets[WIDX_SIMPLEX_BASE_FREQ_DOWN].type = state ? WindowWidgetType::Button : WindowWidgetType::Empty; + BaseWidgets[WIDX_SIMPLEX_OCTAVES].type = state ? WindowWidgetType::Spinner : WindowWidgetType::Empty; + BaseWidgets[WIDX_SIMPLEX_OCTAVES_UP].type = state ? WindowWidgetType::Button : WindowWidgetType::Empty; + BaseWidgets[WIDX_SIMPLEX_OCTAVES_DOWN].type = state ? WindowWidgetType::Button : WindowWidgetType::Empty; + // clang-format on + } + + void ToggleHeightmapWidgets(bool state) + { + // clang-format off + BaseWidgets[WIDX_HEIGHTMAP_GROUP].type = state ? WindowWidgetType::Groupbox : WindowWidgetType::Empty; + BaseWidgets[WIDX_HEIGHTMAP_BROWSE].type = state ? WindowWidgetType::Button : WindowWidgetType::Empty; + BaseWidgets[WIDX_HEIGHTMAP_NORMALIZE].type = state ? WindowWidgetType::Checkbox : WindowWidgetType::Empty; + BaseWidgets[WIDX_HEIGHTMAP_SMOOTH_HEIGHTMAP].type = state ? WindowWidgetType::Checkbox : WindowWidgetType::Empty; + BaseWidgets[WIDX_HEIGHTMAP_STRENGTH].type = state ? WindowWidgetType::Spinner : WindowWidgetType::Empty; + BaseWidgets[WIDX_HEIGHTMAP_STRENGTH_UP].type = state ? WindowWidgetType::Button : WindowWidgetType::Empty; + BaseWidgets[WIDX_HEIGHTMAP_STRENGTH_DOWN].type = state ? WindowWidgetType::Button : WindowWidgetType::Empty; + // clang-format on + } + + void BaseDraw(DrawPixelInfo& dpi) + { + DrawWidgets(dpi); + DrawTabImages(dpi); + + if (_settings.algorithm == MapGenAlgorithm::simplexNoise) + SimplexDraw(dpi); + + else if (_settings.algorithm == MapGenAlgorithm::heightmapImage) + HeightmapDraw(dpi); + + const auto enabledColour = colours[1]; + const auto disabledColour = enabledColour.withFlag(ColourFlag::inset, true); + + { + auto textColour = IsWidgetDisabled(WIDX_MAP_SIZE_Y) ? disabledColour : enabledColour; + DrawTextBasic( + dpi, windowPos + ScreenCoordsXY{ 10, widgets[WIDX_MAP_SIZE_Y].top + 1 }, STR_MAP_SIZE, {}, { textColour }); + } + + { + auto textColour = enabledColour; + DrawTextBasic( + dpi, windowPos + ScreenCoordsXY{ 10, widgets[WIDX_HEIGHTMAP_SOURCE].top + 1 }, STR_HEIGHTMAP_SOURCE, {}, + { textColour }); + } + } + +#pragma endregion + +#pragma region Forests page + + void ForestsMouseUp(WidgetIndex widgetIndex) + { + SharedMouseUp(widgetIndex); + + switch (widgetIndex) + { + case WIDX_FORESTS_PLACE_TREES: + _settings.trees ^= true; + Invalidate(); + break; + + case WIDX_TREE_LAND_RATIO: + { + Formatter ft; + ft.Add(1); + ft.Add(50); + WindowTextInputOpen( + this, widgetIndex, STR_TREE_TO_LAND_RATIO, STR_ENTER_TREE_TO_LAND_RATIO, ft, STR_FORMAT_INTEGER, + _settings.treeToLandRatio, 2); + break; + } + + case WIDX_TREE_ALTITUDE_MIN: + { + Formatter ft; + ft.Add(BaseZToMetres(kMinimumLandHeight)); + ft.Add(BaseZToMetres(kMaximumLandHeight)); + WindowTextInputOpen( + this, widgetIndex, STR_MIN_TREE_ALTITUDE, STR_ENTER_MIN_TREE_ALTITUDE, ft, STR_FORMAT_INTEGER, + BaseZToMetres(_settings.minTreeAltitude), 6); + break; + } + + case WIDX_TREE_ALTITUDE_MAX: + { + Formatter ft; + ft.Add(BaseZToMetres(kMinimumLandHeight)); + ft.Add(BaseZToMetres(kMaximumLandHeight)); + WindowTextInputOpen( + this, widgetIndex, STR_MAX_TREE_ALTITUDE, STR_ENTER_MAX_TREE_ALTITUDE, ft, STR_FORMAT_INTEGER, + BaseZToMetres(_settings.maxTreeAltitude), 6); + break; + } + } + } + + void ForestsMouseDown(WidgetIndex widgetIndex, Widget* widget) + { + switch (widgetIndex) + { + case WIDX_TREE_LAND_RATIO_UP: + _settings.treeToLandRatio = std::min(_settings.treeToLandRatio + 1, 50); + InvalidateWidget(WIDX_TREE_LAND_RATIO); + break; + case WIDX_TREE_LAND_RATIO_DOWN: + _settings.treeToLandRatio = std::max(_settings.treeToLandRatio - 1, 1); + InvalidateWidget(WIDX_TREE_LAND_RATIO); + break; + case WIDX_TREE_ALTITUDE_MIN_UP: + _settings.minTreeAltitude = std::min(_settings.minTreeAltitude + 2, kMaximumLandHeight / 2 - 1); + _settings.maxTreeAltitude = std::max(_settings.maxTreeAltitude, _settings.minTreeAltitude + 2); + InvalidateWidget(WIDX_TREE_ALTITUDE_MIN); + break; + case WIDX_TREE_ALTITUDE_MIN_DOWN: + _settings.minTreeAltitude = std::max(_settings.minTreeAltitude - 2, kMinimumLandHeight); + InvalidateWidget(WIDX_TREE_ALTITUDE_MIN); + break; + case WIDX_TREE_ALTITUDE_MAX_UP: + _settings.maxTreeAltitude = std::min(_settings.maxTreeAltitude + 2, kMaximumLandHeight - 1); + InvalidateWidget(WIDX_TREE_ALTITUDE_MAX); + break; + case WIDX_TREE_ALTITUDE_MAX_DOWN: + _settings.maxTreeAltitude = std::max(_settings.maxTreeAltitude - 2, kMinimumLandHeight - 1); + _settings.minTreeAltitude = std::min(_settings.minTreeAltitude, _settings.maxTreeAltitude - 2); + InvalidateWidget(WIDX_TREE_ALTITUDE_MAX); + break; + } + } + + void ForestsUpdate() + { + // Tab animation + if (++frame_no >= TabAnimationLoops[page]) + frame_no = 0; + InvalidateWidget(WIDX_TAB_2); + } + + void ForestsTextInput(WidgetIndex widgetIndex, int32_t rawValue, int32_t value) + { + switch (widgetIndex) + { + case WIDX_TREE_LAND_RATIO: + _settings.treeToLandRatio = std::clamp(rawValue, 1, 50); + break; + + case WIDX_TREE_ALTITUDE_MIN: + _settings.minTreeAltitude = value; + _settings.maxTreeAltitude = std::max(_settings.minTreeAltitude, _settings.maxTreeAltitude); + break; + + case WIDX_TREE_ALTITUDE_MAX: + _settings.maxTreeAltitude = value; + _settings.minTreeAltitude = std::min(_settings.minTreeAltitude, _settings.maxTreeAltitude); + break; + } + + Invalidate(); + } + + void ForestsPrepareDraw() + { + if (widgets != PageWidgets[WINDOW_MAPGEN_PAGE_FORESTS]) + { + widgets = PageWidgets[WINDOW_MAPGEN_PAGE_FORESTS]; + InitScrollWidgets(); + } + + pressed_widgets = 0; + if (_settings.trees) + pressed_widgets |= 1uLL << WIDX_FORESTS_PLACE_TREES; + + SetPressedTab(); + + const bool isFlatland = _settings.algorithm == MapGenAlgorithm::blank; + + SetWidgetDisabled(WIDX_TREE_LAND_RATIO, !_settings.trees); + SetWidgetDisabled(WIDX_TREE_LAND_RATIO_UP, !_settings.trees); + SetWidgetDisabled(WIDX_TREE_LAND_RATIO_DOWN, !_settings.trees); + SetWidgetDisabled(WIDX_TREE_ALTITUDE_MIN, !_settings.trees); + SetWidgetDisabled(WIDX_TREE_ALTITUDE_MIN_UP, !_settings.trees); + SetWidgetDisabled(WIDX_TREE_ALTITUDE_MIN_DOWN, !_settings.trees); + SetWidgetDisabled(WIDX_TREE_ALTITUDE_MAX, !_settings.trees || isFlatland); + SetWidgetDisabled(WIDX_TREE_ALTITUDE_MAX_UP, !_settings.trees || isFlatland); + SetWidgetDisabled(WIDX_TREE_ALTITUDE_MAX_DOWN, !_settings.trees || isFlatland); + } + + void ForestsDraw(DrawPixelInfo& dpi) + { + DrawWidgets(dpi); + DrawTabImages(dpi); + + const auto enabledColour = colours[1]; + const auto disabledColour = enabledColour.withFlag(ColourFlag::inset, true); + + const auto textColour = _settings.trees ? enabledColour : disabledColour; + + // Tree to land ratio, label and value + DrawTextBasic( + dpi, windowPos + ScreenCoordsXY{ 10, widgets[WIDX_TREE_LAND_RATIO].top + 1 }, STR_MAPGEN_TREE_TO_LAND_RATIO, {}, + { textColour }); + + auto ft = Formatter(); + ft.Add(_settings.treeToLandRatio); + DrawTextBasic( + dpi, + windowPos + ScreenCoordsXY{ widgets[WIDX_TREE_LAND_RATIO].left + 1, widgets[WIDX_TREE_LAND_RATIO].top + 1 }, + STR_MAPGEN_TREE_TO_LAND_RATIO_PCT, ft, { textColour }); + + // Minimum tree altitude, label and value + DrawTextBasic( + dpi, windowPos + ScreenCoordsXY{ 10, widgets[WIDX_TREE_ALTITUDE_MIN].top + 1 }, STR_MAPGEN_TREE_MIN_ALTITUDE, + {}, { textColour }); + + ft = Formatter(); + ft.Add(BaseZToMetres(_settings.minTreeAltitude)); + DrawTextBasic( + dpi, + windowPos + ScreenCoordsXY{ widgets[WIDX_TREE_ALTITUDE_MIN].left + 1, widgets[WIDX_TREE_ALTITUDE_MIN].top + 1 }, + STR_RIDE_LENGTH_ENTRY, ft, { textColour }); + + // Maximum tree altitude, label and value + const bool isFlatland = _settings.algorithm == MapGenAlgorithm::blank; + const auto maxTreeTextColour = _settings.trees && !isFlatland ? enabledColour : disabledColour; + + DrawTextBasic( + dpi, windowPos + ScreenCoordsXY{ 10, widgets[WIDX_TREE_ALTITUDE_MAX].top + 1 }, STR_MAPGEN_TREE_MAX_ALTITUDE, + {}, { maxTreeTextColour }); + + ft = Formatter(); + ft.Add(BaseZToMetres(_settings.maxTreeAltitude)); + DrawTextBasic( + dpi, + windowPos + ScreenCoordsXY{ widgets[WIDX_TREE_ALTITUDE_MAX].left + 1, widgets[WIDX_TREE_ALTITUDE_MAX].top + 1 }, + STR_RIDE_LENGTH_ENTRY, ft, { maxTreeTextColour }); + } + +#pragma endregion + +#pragma region Simplex settings, part of generator tab + + void SimplexMouseUp(WidgetIndex widgetIndex) + { + switch (widgetIndex) + { + case WIDX_SIMPLEX_BASE_FREQ: + { + Formatter ft; + ft.Add(0); + ft.Add(1000); + WindowTextInputOpen( + this, widgetIndex, STR_SIMPLEX_BASE_FREQUENCY, STR_ENTER_BASE_FREQUENCY, ft, STR_FORMAT_COMMA2DP32, + _settings.simplex_base_freq, 4); + break; + } + + case WIDX_SIMPLEX_OCTAVES: + { + Formatter ft; + ft.Add(1); + ft.Add(10); + WindowTextInputOpen( + this, widgetIndex, STR_SIMPLEX_OCTAVES, STR_ENTER_OCTAVES, ft, STR_FORMAT_INTEGER, + _settings.simplex_octaves, 10); + break; + } + } + } + + void SimplexMouseDown(WidgetIndex widgetIndex, Widget* widget) + { + switch (widgetIndex) + { + case WIDX_SIMPLEX_BASE_FREQ_UP: + _settings.simplex_base_freq = std::min(_settings.simplex_base_freq + 5, 1000); + Invalidate(); + break; + case WIDX_SIMPLEX_BASE_FREQ_DOWN: + _settings.simplex_base_freq = std::max(_settings.simplex_base_freq - 5, 0); + Invalidate(); + break; + case WIDX_SIMPLEX_OCTAVES_UP: + _settings.simplex_octaves = std::min(_settings.simplex_octaves + 1, 10); + Invalidate(); + break; + case WIDX_SIMPLEX_OCTAVES_DOWN: + _settings.simplex_octaves = std::max(_settings.simplex_octaves - 1, 1); + Invalidate(); + break; + } + } + + void SimplexDraw(DrawPixelInfo& dpi) + { + DrawWidgets(dpi); + DrawTabImages(dpi); + + const auto textColour = colours[1]; + + DrawTextBasic( + dpi, windowPos + ScreenCoordsXY{ 10, widgets[WIDX_SIMPLEX_BASE_FREQ].top + 1 }, + STR_MAPGEN_SIMPLEX_NOISE_BASE_FREQUENCY, {}, { textColour }); + DrawTextBasic( + dpi, windowPos + ScreenCoordsXY{ 10, widgets[WIDX_SIMPLEX_OCTAVES].top + 1 }, STR_MAPGEN_SIMPLEX_NOISE_OCTAVES, + {}, { textColour }); + + auto ft = Formatter(); + ft.Add(_settings.simplex_base_freq); + DrawTextBasic( + dpi, + windowPos + ScreenCoordsXY{ widgets[WIDX_SIMPLEX_BASE_FREQ].left + 1, widgets[WIDX_SIMPLEX_BASE_FREQ].top + 1 }, + STR_WINDOW_COLOUR_2_COMMA2DP32, ft, { textColour }); + + ft = Formatter(); + ft.Add(_settings.simplex_octaves); + DrawTextBasic( + dpi, + windowPos + ScreenCoordsXY{ widgets[WIDX_SIMPLEX_OCTAVES].left + 1, widgets[WIDX_SIMPLEX_OCTAVES].top + 1 }, + STR_COMMA16, ft, { textColour }); + } + + void SimplexTextInput(WidgetIndex widgetIndex, int32_t value) + { + switch (widgetIndex) + { + case WIDX_SIMPLEX_BASE_FREQ: + _settings.simplex_base_freq = std::clamp(value, 0, 1000); + break; + + case WIDX_SIMPLEX_OCTAVES: + _settings.simplex_octaves = std::clamp(value, 1, 10); + break; + } + } + +#pragma endregion + +#pragma region Heightmap settings, part of generator tab + + void HeightmapMouseDown(WidgetIndex widgetIndex, Widget* widget) + { + switch (widgetIndex) + { + case WIDX_HEIGHTMAP_STRENGTH_UP: + _settings.smooth_strength = std::min(_settings.smooth_strength + 1, 20); + InvalidateWidget(WIDX_HEIGHTMAP_STRENGTH); + break; + case WIDX_HEIGHTMAP_STRENGTH_DOWN: + _settings.smooth_strength = std::max(_settings.smooth_strength - 1, 1); + InvalidateWidget(WIDX_HEIGHTMAP_STRENGTH); + break; + } + } + + void HeightmapMouseUp(WidgetIndex widgetIndex) + { + switch (widgetIndex) + { + // Page widgets + case WIDX_HEIGHTMAP_BROWSE: + { + auto intent = Intent(WindowClass::Loadsave); + intent.PutExtra(INTENT_EXTRA_LOADSAVE_TYPE, LOADSAVETYPE_LOAD | LOADSAVETYPE_HEIGHTMAP); + intent.PutExtra(INTENT_EXTRA_CALLBACK, reinterpret_cast(HeightmapLoadsaveCallback)); + ContextOpenIntent(&intent); + return; + } + case WIDX_HEIGHTMAP_SMOOTH_HEIGHTMAP: + _settings.smooth_height_map = !_settings.smooth_height_map; + SetCheckboxValue(WIDX_HEIGHTMAP_SMOOTH_HEIGHTMAP, _settings.smooth_height_map); + SetWidgetEnabled(WIDX_HEIGHTMAP_STRENGTH, _settings.smooth_height_map); + SetWidgetEnabled(WIDX_HEIGHTMAP_STRENGTH_UP, _settings.smooth_height_map); + SetWidgetEnabled(WIDX_HEIGHTMAP_STRENGTH_DOWN, _settings.smooth_height_map); + InvalidateWidget(WIDX_HEIGHTMAP_SMOOTH_HEIGHTMAP); + InvalidateWidget(WIDX_HEIGHTMAP_STRENGTH); + break; + + case WIDX_HEIGHTMAP_NORMALIZE: + _settings.normalize_height = !_settings.normalize_height; + SetCheckboxValue(WIDX_HEIGHTMAP_NORMALIZE, _settings.normalize_height); + InvalidateWidget(WIDX_HEIGHTMAP_NORMALIZE); + break; + + case WIDX_HEIGHTMAP_STRENGTH: + { + Formatter ft; + ft.Add(1); + ft.Add(20); + WindowTextInputOpen( + this, widgetIndex, STR_SMOOTH_STRENGTH, STR_ENTER_SMOOTH_STRENGTH, ft, STR_FORMAT_INTEGER, + _settings.smooth_strength, 2); + break; + } + } + } + + void HeightmapPrepareDraw() + { + SetCheckboxValue(WIDX_HEIGHTMAP_SMOOTH_HEIGHTMAP, _settings.smooth_height_map); + SetCheckboxValue(WIDX_HEIGHTMAP_NORMALIZE, _settings.normalize_height); + } + + void HeightmapDraw(DrawPixelInfo& dpi) + { + const auto enabledColour = colours[1]; + const auto disabledColour = enabledColour.withFlag(ColourFlag::inset, true); + + // Smooth strength label and value + const bool strengthDisabled = IsWidgetDisabled(WIDX_HEIGHTMAP_STRENGTH) || !_settings.smooth_height_map; + const auto strengthColour = strengthDisabled ? disabledColour : enabledColour; + + // Smooth strength label + DrawTextBasic( + dpi, windowPos + ScreenCoordsXY{ 24, widgets[WIDX_HEIGHTMAP_STRENGTH].top + 1 }, STR_MAPGEN_SMOOTH_STRENGTH, {}, + { strengthColour }); + + // Smooth strength value + auto ft = Formatter(); + ft.Add(_settings.smooth_strength); + auto pos = ScreenCoordsXY{ widgets[WIDX_HEIGHTMAP_STRENGTH].left + 1, widgets[WIDX_HEIGHTMAP_STRENGTH].top + 1 }; + DrawTextBasic(dpi, windowPos + pos, STR_COMMA16, ft, { strengthColour }); + + // Current heightmap image filename + ft = Formatter(); + if (!_heightmapLoaded) + ft.Add(LanguageGetString(STR_MAPGEN_NONE_SELECTED)); + else + ft.Add(_heightmapFilename.c_str()); + + pos = ScreenCoordsXY{ 10, widgets[WIDX_HEIGHTMAP_BROWSE].top + 1 }; + auto textWidth = widgets[WIDX_HEIGHTMAP_BROWSE].left - 11; + DrawTextEllipsised(dpi, windowPos + pos, textWidth, STR_MAPGEN_CURRENT_HEIGHTMAP_FILE, ft); + } + + void HeightmapTextInput(WidgetIndex widgetIndex, int32_t value) + { + switch (widgetIndex) + { + case WIDX_HEIGHTMAP_STRENGTH: + _settings.smooth_strength = std::clamp(value, 1, 20); + break; + } + } + +#pragma endregion + +#pragma region Terrain page + + void TerrainMouseUp(WidgetIndex widgetIndex) + { + SharedMouseUp(widgetIndex); + + switch (widgetIndex) + { + case WIDX_HEIGHTMAP_LOW: + { + Formatter ft; + ft.Add(BaseZToMetres(kMinimumLandHeight)); + ft.Add(BaseZToMetres(kMaximumLandHeight)); + WindowTextInputOpen( + this, widgetIndex, STR_MIN_LAND_HEIGHT, STR_ENTER_MIN_LAND, ft, STR_FORMAT_INTEGER, + BaseZToMetres(_settings.heightmapLow), 6); + break; + } + + case WIDX_HEIGHTMAP_HIGH: + { + Formatter ft; + ft.Add(BaseZToMetres(kMinimumLandHeight)); + ft.Add(BaseZToMetres(kMaximumLandHeight)); + WindowTextInputOpen( + this, widgetIndex, STR_MAX_LAND_HEIGHT, STR_ENTER_MAX_LAND, ft, STR_FORMAT_INTEGER, + BaseZToMetres(_settings.heightmapHigh), 6); + break; + } + + case WIDX_HEIGHTMAP_SMOOTH_TILE_EDGES: + _settings.smoothTileEdges = !_settings.smoothTileEdges; + SetCheckboxValue(WIDX_HEIGHTMAP_SMOOTH_TILE_EDGES, _settings.smoothTileEdges); + InvalidateWidget(WIDX_HEIGHTMAP_SMOOTH_TILE_EDGES); + break; + } + } + + void TerrainMouseDown(WidgetIndex widgetIndex, Widget* widget) + { + switch (widgetIndex) + { + case WIDX_RANDOM_TERRAIN: + _randomTerrain = !_randomTerrain; + Invalidate(); + break; + case WIDX_FLOOR_TEXTURE: + LandTool::ShowSurfaceStyleDropdown(this, widget, _settings.landTexture); + break; + case WIDX_WALL_TEXTURE: + LandTool::ShowEdgeStyleDropdown(this, widget, _settings.edgeTexture); + break; + case WIDX_HEIGHTMAP_LOW_UP: + _settings.heightmapLow = std::min(_settings.heightmapLow + 2, kMaximumLandHeight / 2 - 1); + _settings.heightmapHigh = std::max(_settings.heightmapHigh, _settings.heightmapLow + 2); + InvalidateWidget(WIDX_HEIGHTMAP_LOW); + break; + case WIDX_HEIGHTMAP_LOW_DOWN: + _settings.heightmapLow = std::max(_settings.heightmapLow - 2, kMinimumLandHeight); + InvalidateWidget(WIDX_HEIGHTMAP_LOW); + break; + case WIDX_HEIGHTMAP_HIGH_UP: + _settings.heightmapHigh = std::min(_settings.heightmapHigh + 2, kMaximumLandHeight - 1); + InvalidateWidget(WIDX_HEIGHTMAP_HIGH); + break; + case WIDX_HEIGHTMAP_HIGH_DOWN: + _settings.heightmapHigh = std::max(_settings.heightmapHigh - 2, kMinimumLandHeight); + _settings.heightmapLow = std::min(_settings.heightmapLow, _settings.heightmapHigh - 2); + InvalidateWidget(WIDX_HEIGHTMAP_HIGH); + break; + } + } + + void TerrainUpdate() + { + // Tab animation + if (++frame_no >= TabAnimationLoops[page]) + frame_no = 0; + InvalidateWidget(WIDX_TAB_3); + } + + void TerrainTextInput(WidgetIndex widgetIndex, int32_t value) + { + switch (widgetIndex) + { + case WIDX_HEIGHTMAP_LOW: + _settings.heightmapLow = value; + _settings.heightmapHigh = std::max(_settings.heightmapLow, _settings.heightmapHigh); + break; + + case WIDX_HEIGHTMAP_HIGH: + _settings.heightmapHigh = value; + _settings.heightmapLow = std::min(_settings.heightmapLow, _settings.heightmapHigh); + break; + } + + Invalidate(); + } + + void TerrainDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) + { + int32_t type; + + switch (widgetIndex) + { + case WIDX_FLOOR_TEXTURE: + if (dropdownIndex == -1) + dropdownIndex = gDropdownHighlightedIndex; + + type = (dropdownIndex == -1) ? _settings.landTexture : dropdownIndex; + + if (gLandToolTerrainSurface == type) + { + gLandToolTerrainSurface = OBJECT_ENTRY_INDEX_NULL; + } + else + { + gLandToolTerrainSurface = type; + _settings.landTexture = type; + } + Invalidate(); + break; + case WIDX_WALL_TEXTURE: + if (dropdownIndex == -1) + dropdownIndex = gDropdownHighlightedIndex; + + type = (dropdownIndex == -1) ? _settings.edgeTexture : dropdownIndex; + + if (gLandToolTerrainEdge == type) + { + gLandToolTerrainEdge = OBJECT_ENTRY_INDEX_NULL; + } + else + { + gLandToolTerrainEdge = type; + _settings.edgeTexture = type; + } + Invalidate(); + break; + } } void DrawDropdownButton(DrawPixelInfo& dpi, WidgetIndex widgetIndex, ImageId image) @@ -674,7 +1194,7 @@ namespace OpenRCT2::Ui::Windows { auto& objManager = GetContext()->GetObjectManager(); const auto surfaceObj = static_cast( - objManager.GetLoadedObject(ObjectType::TerrainSurface, _floorTexture)); + objManager.GetLoadedObject(ObjectType::TerrainSurface, _settings.landTexture)); ImageId surfaceImage; if (surfaceObj != nullptr) { @@ -687,7 +1207,7 @@ namespace OpenRCT2::Ui::Windows ImageId edgeImage; const auto edgeObj = static_cast( - objManager.GetLoadedObject(ObjectType::TerrainEdge, _wallTexture)); + objManager.GetLoadedObject(ObjectType::TerrainEdge, _settings.edgeTexture)); if (edgeObj != nullptr) { edgeImage = ImageId(edgeObj->IconImageId); @@ -697,561 +1217,162 @@ namespace OpenRCT2::Ui::Windows DrawDropdownButton(dpi, edgeWidgetIndex, edgeImage); } - void BaseDraw(DrawPixelInfo& dpi) + void TerrainPrepareDraw() + { + if (widgets != PageWidgets[WINDOW_MAPGEN_PAGE_TERRAIN]) + { + widgets = PageWidgets[WINDOW_MAPGEN_PAGE_TERRAIN]; + InitScrollWidgets(); + } + + SetCheckboxValue(WIDX_RANDOM_TERRAIN, _randomTerrain != 0); + SetCheckboxValue(WIDX_HEIGHTMAP_SMOOTH_TILE_EDGES, _settings.smoothTileEdges); + + // Only allow floor and wall texture options if random terrain is disabled + SetWidgetEnabled(WIDX_FLOOR_TEXTURE, !_randomTerrain); + SetWidgetEnabled(WIDX_WALL_TEXTURE, !_randomTerrain); + + // Max land height option is irrelevant for flatland + SetWidgetEnabled(WIDX_HEIGHTMAP_HIGH, _settings.algorithm != MapGenAlgorithm::blank); + + // Only offer terrain edge smoothing if we don't use flatland terrain + SetWidgetEnabled(WIDX_HEIGHTMAP_SMOOTH_TILE_EDGES, _settings.algorithm != MapGenAlgorithm::blank); + SetPressedTab(); + } + + void TerrainDraw(DrawPixelInfo& dpi) { DrawWidgets(dpi); DrawTabImages(dpi); DrawDropdownButtons(dpi, WIDX_FLOOR_TEXTURE, WIDX_WALL_TEXTURE); - const auto textColour = colours[1]; - - DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ 4, widgets[WIDX_MAP_SIZE_Y].top + 1 }, STR_MAP_SIZE, {}, { textColour }); - DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ 4, widgets[WIDX_BASE_HEIGHT].top + 1 }, STR_BASE_HEIGHT_LABEL, {}, - { textColour }); - DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ 4, widgets[WIDX_WATER_LEVEL].top + 1 }, STR_WATER_LEVEL_LABEL, {}, - { textColour }); - DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ 4, widgets[WIDX_FLOOR_TEXTURE].top + 1 }, STR_TERRAIN_LABEL, {}, - { textColour }); - - auto ft = Formatter(); - ft.Add((_baseHeight - 12) / 2); - DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ widgets[WIDX_BASE_HEIGHT].left + 1, widgets[WIDX_BASE_HEIGHT].top + 1 }, - STR_COMMA16, ft, { colours[1] }); - - ft = Formatter(); - ft.Add((_waterLevel - 12) / 2); - DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ widgets[WIDX_WATER_LEVEL].left + 1, widgets[WIDX_WATER_LEVEL].top + 1 }, - STR_COMMA16, ft, { colours[1] }); - } - -#pragma endregion - -#pragma region Random page - - void RandomMouseUp(WidgetIndex widgetIndex) - { - SharedMouseUp(widgetIndex); - - MapGenSettings mapgenSettings; - - switch (widgetIndex) - { - case WIDX_RANDOM_GENERATE: - mapgenSettings.mapSize = _mapSize; - mapgenSettings.height = _baseHeight + 2; - mapgenSettings.water_level = _waterLevel + 2; - mapgenSettings.floor = _randomTerrain ? -1 : _floorTexture; - mapgenSettings.wall = _randomTerrain ? -1 : _wallTexture; - mapgenSettings.trees = _placeTrees; - - mapgenSettings.simplex_low = UtilRand() % 4; - mapgenSettings.simplex_high = 12 + (UtilRand() % (32 - 12)); - mapgenSettings.simplex_base_freq = 1.75f; - mapgenSettings.simplex_octaves = 6; - - MapGenGenerate(&mapgenSettings); - GfxInvalidateScreen(); - break; - case WIDX_RANDOM_TERRAIN: - _randomTerrain = !_randomTerrain; - break; - case WIDX_RANDOM_PLACE_TREES: - _placeTrees ^= 1; - break; - } - } - - void RandomUpdate() - { - // Tab animation - if (++frame_no >= TabAnimationLoops[page]) - frame_no = 0; - InvalidateWidget(WIDX_TAB_2); - } - - void RandomPrepareDraw() - { - if (widgets != PageWidgets[WINDOW_MAPGEN_PAGE_RANDOM]) - { - widgets = PageWidgets[WINDOW_MAPGEN_PAGE_RANDOM]; - InitScrollWidgets(); - } - - pressed_widgets = 0; - if (_randomTerrain) - pressed_widgets |= 1uLL << WIDX_RANDOM_TERRAIN; - if (_placeTrees) - pressed_widgets |= 1uLL << WIDX_RANDOM_PLACE_TREES; - - SetPressedTab(); - } - - void RandomDraw(DrawPixelInfo& dpi) - { - DrawWidgets(dpi); - DrawTabImages(dpi); - } - -#pragma endregion - -#pragma region Simplex page - - void SimplexMouseUp(WidgetIndex widgetIndex) - { - SharedMouseUp(widgetIndex); - - MapGenSettings mapgenSettings; - - switch (widgetIndex) - { - case WIDX_SIMPLEX_MAP_SIZE_Y: - _resizeDirection = ResizeDirection::Y; - InputMapSize(WIDX_SIMPLEX_MAP_SIZE_Y, _mapSize.y); - break; - case WIDX_SIMPLEX_MAP_SIZE_X: - _resizeDirection = ResizeDirection::X; - InputMapSize(WIDX_SIMPLEX_MAP_SIZE_X, _mapSize.x); - break; - case WIDX_SIMPLEX_MAP_SIZE_LINK: - _mapWidthAndHeightLinked = !_mapWidthAndHeightLinked; - break; - case WIDX_SIMPLEX_GENERATE: - mapgenSettings.mapSize = _mapSize; - - mapgenSettings.height = _baseHeight; - mapgenSettings.water_level = _waterLevel + kMinimumWaterHeight; - mapgenSettings.floor = _randomTerrain ? -1 : _floorTexture; - mapgenSettings.wall = _randomTerrain ? -1 : _wallTexture; - mapgenSettings.trees = _placeTrees; - - mapgenSettings.simplex_low = _simplex_low; - mapgenSettings.simplex_high = _simplex_high; - mapgenSettings.simplex_base_freq = (static_cast(_simplex_base_freq)) / 100.00f; - mapgenSettings.simplex_octaves = _simplex_octaves; - - MapGenGenerate(&mapgenSettings); - GfxInvalidateScreen(); - break; - } - } - - void SimplexMouseDown(WidgetIndex widgetIndex, Widget* widget) - { - switch (widgetIndex) - { - case WIDX_SIMPLEX_LOW_UP: - _simplex_low = std::min(_simplex_low + 1, 24); - Invalidate(); - break; - case WIDX_SIMPLEX_LOW_DOWN: - _simplex_low = std::max(_simplex_low - 1, 0); - Invalidate(); - break; - case WIDX_SIMPLEX_HIGH_UP: - _simplex_high = std::min(_simplex_high + 1, 36); - Invalidate(); - break; - case WIDX_SIMPLEX_HIGH_DOWN: - _simplex_high = std::max(_simplex_high - 1, 0); - Invalidate(); - break; - case WIDX_SIMPLEX_BASE_FREQ_UP: - _simplex_base_freq = std::min(_simplex_base_freq + 5, 1000); - Invalidate(); - break; - case WIDX_SIMPLEX_BASE_FREQ_DOWN: - _simplex_base_freq = std::max(_simplex_base_freq - 5, 0); - Invalidate(); - break; - case WIDX_SIMPLEX_OCTAVES_UP: - _simplex_octaves = std::min(_simplex_octaves + 1, 10); - Invalidate(); - break; - case WIDX_SIMPLEX_OCTAVES_DOWN: - _simplex_octaves = std::max(_simplex_octaves - 1, 1); - Invalidate(); - break; - case WIDX_SIMPLEX_MAP_SIZE_Y_UP: - _resizeDirection = ResizeDirection::Y; - ChangeMapSize(+1); - Invalidate(); - break; - case WIDX_SIMPLEX_MAP_SIZE_Y_DOWN: - _resizeDirection = ResizeDirection::Y; - ChangeMapSize(-1); - Invalidate(); - break; - case WIDX_SIMPLEX_MAP_SIZE_X_UP: - _resizeDirection = ResizeDirection::X; - ChangeMapSize(+1); - Invalidate(); - break; - case WIDX_SIMPLEX_MAP_SIZE_X_DOWN: - _resizeDirection = ResizeDirection::X; - ChangeMapSize(-1); - Invalidate(); - break; - case WIDX_SIMPLEX_WATER_LEVEL_UP: - _waterLevel = std::min(_waterLevel + kMinimumWaterHeight, kMinimumWaterHeight + kMaximumWaterHeight); - Invalidate(); - break; - case WIDX_SIMPLEX_WATER_LEVEL_DOWN: - _waterLevel = std::max(_waterLevel - kMinimumWaterHeight, 0); - Invalidate(); - break; - case WIDX_SIMPLEX_RANDOM_TERRAIN_CHECKBOX: - _randomTerrain = !_randomTerrain; - Invalidate(); - break; - case WIDX_SIMPLEX_FLOOR_TEXTURE: - LandTool::ShowSurfaceStyleDropdown(this, widget, _floorTexture); - break; - case WIDX_SIMPLEX_WALL_TEXTURE: - LandTool::ShowEdgeStyleDropdown(this, widget, _wallTexture); - break; - case WIDX_SIMPLEX_PLACE_TREES_CHECKBOX: - _placeTrees ^= 1; - Invalidate(); - break; - } - } - - void SimplexDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) - { - int32_t type; - - switch (widgetIndex) - { - case WIDX_SIMPLEX_FLOOR_TEXTURE: - if (dropdownIndex == -1) - dropdownIndex = gDropdownHighlightedIndex; - - type = (dropdownIndex == -1) ? _floorTexture : dropdownIndex; - - if (gLandToolTerrainSurface == type) - { - gLandToolTerrainSurface = OBJECT_ENTRY_INDEX_NULL; - } - else - { - gLandToolTerrainSurface = type; - _floorTexture = type; - } - Invalidate(); - break; - case WIDX_SIMPLEX_WALL_TEXTURE: - if (dropdownIndex == -1) - dropdownIndex = gDropdownHighlightedIndex; - - type = (dropdownIndex == -1) ? _wallTexture : dropdownIndex; - - if (gLandToolTerrainEdge == type) - { - gLandToolTerrainEdge = OBJECT_ENTRY_INDEX_NULL; - } - else - { - gLandToolTerrainEdge = type; - _wallTexture = type; - } - Invalidate(); - break; - } - } - - void SimplexUpdate() - { - // Tab animation - if (++frame_no >= TabAnimationLoops[page]) - frame_no = 0; - InvalidateWidget(WIDX_TAB_3); - } - - void SimplexPrepareDraw() - { - if (widgets != PageWidgets[WINDOW_MAPGEN_PAGE_SIMPLEX]) - { - widgets = PageWidgets[WINDOW_MAPGEN_PAGE_SIMPLEX]; - InitScrollWidgets(); - } - - // Only allow linking the map size when X and Y are the same - SetWidgetPressed(WIDX_SIMPLEX_MAP_SIZE_LINK, _mapWidthAndHeightLinked); - SetWidgetDisabled(WIDX_SIMPLEX_MAP_SIZE_LINK, _mapSize.x != _mapSize.y); - - SetCheckboxValue(WIDX_SIMPLEX_RANDOM_TERRAIN_CHECKBOX, _randomTerrain != 0); - SetCheckboxValue(WIDX_SIMPLEX_PLACE_TREES_CHECKBOX, _placeTrees != 0); - - // Only allow floor and wall texture options if random terrain is disabled - if (!_randomTerrain) - { - SetWidgetEnabled(WIDX_SIMPLEX_FLOOR_TEXTURE, true); - SetWidgetEnabled(WIDX_SIMPLEX_WALL_TEXTURE, true); - } - else - { - SetWidgetEnabled(WIDX_SIMPLEX_FLOOR_TEXTURE, false); - SetWidgetEnabled(WIDX_SIMPLEX_WALL_TEXTURE, false); - } - - SetPressedTab(); - - // Push width (Y) and height (X) to the common formatter arguments for the map size spinners to use - auto ft = Formatter::Common(); - ft.Add(_mapSize.y - 2); - ft.Add(_mapSize.x - 2); - } - - void SimplexDraw(DrawPixelInfo& dpi) - { - DrawWidgets(dpi); - DrawTabImages(dpi); - DrawDropdownButtons(dpi, WIDX_SIMPLEX_FLOOR_TEXTURE, WIDX_SIMPLEX_WALL_TEXTURE); - - const auto textColour = colours[1]; - - DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ 5, widgets[WIDX_SIMPLEX_LOW].top + 1 }, STR_MAPGEN_SIMPLEX_NOISE_LOW_, {}, - { textColour }); - DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ 5, widgets[WIDX_SIMPLEX_HIGH].top + 1 }, STR_MAPGEN_SIMPLEX_NOISE_HIGH, {}, - { textColour }); - DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ 5, widgets[WIDX_SIMPLEX_BASE_FREQ].top + 1 }, - STR_MAPGEN_SIMPLEX_NOISE_BASE_FREQUENCY, {}, { textColour }); - DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ 5, widgets[WIDX_SIMPLEX_OCTAVES].top + 1 }, STR_MAPGEN_SIMPLEX_NOISE_OCTAVES, - {}, { textColour }); - DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ 5, widgets[WIDX_SIMPLEX_MAP_SIZE_Y].top + 1 }, STR_MAP_SIZE, {}, - { textColour }); - DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ 5, widgets[WIDX_SIMPLEX_WATER_LEVEL].top + 1 }, STR_WATER_LEVEL_LABEL, {}, - { textColour }); - - auto ft = Formatter(); - ft.Add(_simplex_low); - DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ widgets[WIDX_SIMPLEX_LOW].left + 1, widgets[WIDX_SIMPLEX_LOW].top + 1 }, - STR_COMMA16, ft, { textColour }); - ft = Formatter(); - ft.Add(_simplex_high); - DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ widgets[WIDX_SIMPLEX_HIGH].left + 1, widgets[WIDX_SIMPLEX_HIGH].top + 1 }, - STR_COMMA16, ft, { textColour }); - ft = Formatter(); - ft.Add(_simplex_base_freq); - DrawTextBasic( - dpi, - windowPos + ScreenCoordsXY{ widgets[WIDX_SIMPLEX_BASE_FREQ].left + 1, widgets[WIDX_SIMPLEX_BASE_FREQ].top + 1 }, - STR_WINDOW_COLOUR_2_COMMA2DP32, ft, { textColour }); - ft = Formatter(); - ft.Add(_simplex_octaves); - DrawTextBasic( - dpi, - windowPos + ScreenCoordsXY{ widgets[WIDX_SIMPLEX_OCTAVES].left + 1, widgets[WIDX_SIMPLEX_OCTAVES].top + 1 }, - STR_COMMA16, ft, { textColour }); - DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ 5, widgets[WIDX_SIMPLEX_RANDOM_TERRAIN_CHECKBOX].top + 1 }, STR_TERRAIN_LABEL, - {}, { textColour }); - DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ 5, widgets[WIDX_SIMPLEX_PLACE_TREES_CHECKBOX].top + 1 }, - STR_MAPGEN_OPTION_PLACE_TREES, {}, { textColour }); - - ft = Formatter(); - ft.Add((_waterLevel - 12) / 2); - DrawTextBasic( - dpi, - windowPos - + ScreenCoordsXY{ widgets[WIDX_SIMPLEX_WATER_LEVEL].left + 1, widgets[WIDX_SIMPLEX_WATER_LEVEL].top + 1 }, - STR_COMMA16, ft, { textColour }); - } - -#pragma endregion - -#pragma region Heightmap page - - void HeightmapMouseDown(WidgetIndex widgetIndex, Widget* widget) - { - switch (widgetIndex) - { - case WIDX_HEIGHTMAP_STRENGTH_UP: - _heightmapSmoothStrength = std::min(_heightmapSmoothStrength + 1, MAX_SMOOTH_ITERATIONS); - InvalidateWidget(WIDX_HEIGHTMAP_STRENGTH); - break; - case WIDX_HEIGHTMAP_STRENGTH_DOWN: - _heightmapSmoothStrength = std::max(_heightmapSmoothStrength - 1, 1); - InvalidateWidget(WIDX_HEIGHTMAP_STRENGTH); - break; - case WIDX_HEIGHTMAP_LOW_UP: - _heightmapLow = std::min(_heightmapLow + 1, kMaximumWaterHeight - 1); - _heightmapHigh = std::max(_heightmapHigh, _heightmapLow + 1); - InvalidateWidget(WIDX_HEIGHTMAP_LOW); - break; - case WIDX_HEIGHTMAP_LOW_DOWN: - _heightmapLow = std::max(_heightmapLow - 1, 2); - InvalidateWidget(WIDX_HEIGHTMAP_LOW); - break; - case WIDX_HEIGHTMAP_HIGH_UP: - _heightmapHigh = std::min(_heightmapHigh + 1, kMaximumWaterHeight); - InvalidateWidget(WIDX_HEIGHTMAP_HIGH); - break; - case WIDX_HEIGHTMAP_HIGH_DOWN: - _heightmapHigh = std::max(_heightmapHigh - 1, 2 + 1); - _heightmapLow = std::min(_heightmapLow, _heightmapHigh - 1); - InvalidateWidget(WIDX_HEIGHTMAP_HIGH); - break; - case WIDX_HEIGHTMAP_WATER_LEVEL_UP: - _waterLevel = std::min(_waterLevel + kMinimumWaterHeight, kMinimumWaterHeight + kMaximumWaterHeight); - InvalidateWidget(WIDX_HEIGHTMAP_WATER_LEVEL); - break; - case WIDX_HEIGHTMAP_WATER_LEVEL_DOWN: - _waterLevel = std::max(_waterLevel - kMinimumWaterHeight, 0); - InvalidateWidget(WIDX_HEIGHTMAP_WATER_LEVEL); - break; - } - } - - void HeightmapGenerateMap() - { - MapGenSettings mapgenSettings; - mapgenSettings.water_level = _waterLevel; - mapgenSettings.smooth = _heightmapSmoothTiles; - mapgenSettings.smooth_height_map = _heightmapSmoothMap; - mapgenSettings.smooth_strength = _heightmapSmoothStrength; - mapgenSettings.normalize_height = _heightmapNormalize; - mapgenSettings.simplex_low = _heightmapLow; - mapgenSettings.simplex_high = _heightmapHigh; - MapGenGenerateFromHeightmap(&mapgenSettings); - GfxInvalidateScreen(); - } - - void HeightmapMouseUp(WidgetIndex widgetIndex) - { - SharedMouseUp(widgetIndex); - - switch (widgetIndex) - { - case WIDX_CLOSE: - case WIDX_TAB_1: - case WIDX_TAB_2: - case WIDX_TAB_3: - case WIDX_TAB_4: - return; // Only widgets that change a setting need to regenerate the map - - // Page widgets - case WIDX_HEIGHTMAP_SELECT: - { - auto intent = Intent(WindowClass::Loadsave); - intent.PutExtra(INTENT_EXTRA_LOADSAVE_TYPE, LOADSAVETYPE_LOAD | LOADSAVETYPE_HEIGHTMAP); - intent.PutExtra(INTENT_EXTRA_CALLBACK, reinterpret_cast(HeightmapLoadsaveCallback)); - ContextOpenIntent(&intent); - return; - } - case WIDX_HEIGHTMAP_SMOOTH_HEIGHTMAP: - _heightmapSmoothMap = !_heightmapSmoothMap; - SetCheckboxValue(WIDX_HEIGHTMAP_SMOOTH_HEIGHTMAP, _heightmapSmoothMap); - SetWidgetEnabled(WIDX_HEIGHTMAP_STRENGTH, _heightmapSmoothMap); - SetWidgetEnabled(WIDX_HEIGHTMAP_STRENGTH_UP, _heightmapSmoothMap); - SetWidgetEnabled(WIDX_HEIGHTMAP_STRENGTH_DOWN, _heightmapSmoothMap); - InvalidateWidget(WIDX_HEIGHTMAP_SMOOTH_HEIGHTMAP); - InvalidateWidget(WIDX_HEIGHTMAP_STRENGTH); - break; - case WIDX_HEIGHTMAP_NORMALIZE: - _heightmapNormalize = !_heightmapNormalize; - SetCheckboxValue(WIDX_HEIGHTMAP_NORMALIZE, _heightmapNormalize); - InvalidateWidget(WIDX_HEIGHTMAP_NORMALIZE); - break; - case WIDX_HEIGHTMAP_SMOOTH_TILES: - _heightmapSmoothTiles = !_heightmapSmoothTiles; - SetCheckboxValue(WIDX_HEIGHTMAP_SMOOTH_TILES, _heightmapSmoothTiles); - InvalidateWidget(WIDX_HEIGHTMAP_SMOOTH_TILES); - break; - } - - // Always regenerate the map after one of the page widgets has been changed - HeightmapGenerateMap(); - } - - void HeightmapPrepareDraw() - { - if (widgets != PageWidgets[WINDOW_MAPGEN_PAGE_HEIGHTMAP]) - { - widgets = PageWidgets[WINDOW_MAPGEN_PAGE_HEIGHTMAP]; - InitScrollWidgets(); - } - - SetCheckboxValue(WIDX_HEIGHTMAP_SMOOTH_HEIGHTMAP, _heightmapSmoothMap); - SetCheckboxValue(WIDX_HEIGHTMAP_NORMALIZE, _heightmapNormalize); - SetCheckboxValue(WIDX_HEIGHTMAP_SMOOTH_TILES, _heightmapSmoothTiles); - - SetPressedTab(); - } - - void HeightmapDraw(DrawPixelInfo& dpi) - { - DrawWidgets(dpi); - DrawTabImages(dpi); - const auto enabledColour = colours[1]; const auto disabledColour = enabledColour.withFlag(ColourFlag::inset, true); - // Smooth strength label and value - const auto strengthColour = _heightmapSmoothMap ? enabledColour : disabledColour; + // Floor texture label DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ 5, widgets[WIDX_HEIGHTMAP_STRENGTH].top + 1 }, STR_MAPGEN_SMOOTH_STRENGTH, {}, - { strengthColour }); + dpi, windowPos + ScreenCoordsXY{ 10, widgets[WIDX_FLOOR_TEXTURE].top + 1 }, STR_TERRAIN_LABEL, {}, + { enabledColour }); + + // Minimum land height label and value + DrawTextBasic( + dpi, windowPos + ScreenCoordsXY{ 10, widgets[WIDX_HEIGHTMAP_LOW].top + 1 }, STR_MAPGEN_MIN_LAND_HEIGHT, {}, + { enabledColour }); auto ft = Formatter(); - ft.Add(_heightmapSmoothStrength); - DrawTextBasic( - dpi, - windowPos - + ScreenCoordsXY{ widgets[WIDX_HEIGHTMAP_STRENGTH].left + 1, widgets[WIDX_HEIGHTMAP_STRENGTH].top + 1 }, - STR_COMMA16, ft, { strengthColour }); - - // Low label and value - const auto labelColour = _heightmapLoaded ? enabledColour : disabledColour; - DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ 5, widgets[WIDX_HEIGHTMAP_LOW].top + 1 }, STR_MAPGEN_SIMPLEX_NOISE_LOW_, {}, - { labelColour }); - - ft = Formatter(); - ft.Add(_heightmapLow); + ft.Add(BaseZToMetres(_settings.heightmapLow)); DrawTextBasic( dpi, windowPos + ScreenCoordsXY{ widgets[WIDX_HEIGHTMAP_LOW].left + 1, widgets[WIDX_HEIGHTMAP_LOW].top + 1 }, - STR_COMMA16, ft, { labelColour }); + STR_RIDE_LENGTH_ENTRY, ft, { enabledColour }); - // High label and value + const auto maxLandColour = IsWidgetDisabled(WIDX_HEIGHTMAP_HIGH) ? disabledColour : enabledColour; + + // Maximum land height label and value DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ 5, widgets[WIDX_HEIGHTMAP_HIGH].top + 1 }, STR_MAPGEN_SIMPLEX_NOISE_HIGH, {}, - { labelColour }); + dpi, windowPos + ScreenCoordsXY{ 10, widgets[WIDX_HEIGHTMAP_HIGH].top + 1 }, STR_MAPGEN_MAX_LAND_HEIGHT, {}, + { maxLandColour }); ft = Formatter(); - ft.Add(_heightmapHigh); + ft.Add(BaseZToMetres(_settings.heightmapHigh)); DrawTextBasic( dpi, windowPos + ScreenCoordsXY{ widgets[WIDX_HEIGHTMAP_HIGH].left + 1, widgets[WIDX_HEIGHTMAP_HIGH].top + 1 }, - STR_COMMA16, ft, { labelColour }); + STR_RIDE_LENGTH_ENTRY, ft, { maxLandColour }); + } - // Water level label and value - DrawTextBasic( - dpi, windowPos + ScreenCoordsXY{ 5, widgets[WIDX_HEIGHTMAP_WATER_LEVEL].top + 1 }, STR_WATER_LEVEL_LABEL, {}, - { labelColour }); +#pragma endregion + +#pragma region Water page + + void WaterMouseUp(WidgetIndex widgetIndex) + { + SharedMouseUp(widgetIndex); + + switch (widgetIndex) + { + case WIDX_WATER_LEVEL: + { + Formatter ft; + ft.Add(kMinimumWaterHeight); + ft.Add(kMaximumWaterHeight); + WindowTextInputOpen( + this, WIDX_WATER_LEVEL, STR_WATER_LEVEL, STR_ENTER_WATER_LEVEL, ft, STR_FORMAT_INTEGER, + _settings.waterLevel, 6); + break; + } + + case WIDX_ADD_BEACHES: + { + _settings.beaches ^= true; + Invalidate(); + break; + } + } + } + + void WaterMouseDown(WidgetIndex widgetIndex, Widget* widget) + { + switch (widgetIndex) + { + case WIDX_WATER_LEVEL_UP: + _settings.waterLevel = std::min(_settings.waterLevel + 2, kMaximumWaterHeight); + Invalidate(); + break; + case WIDX_WATER_LEVEL_DOWN: + _settings.waterLevel = std::max(_settings.waterLevel - 2, kMinimumWaterHeight); + Invalidate(); + break; + } + } + + void WaterUpdate() + { + // Tab animation + if (++frame_no >= TabAnimationLoops[page]) + frame_no = 0; + InvalidateWidget(WIDX_TAB_4); + } + + void WaterTextInput(WidgetIndex widgetIndex, int32_t value) + { + switch (widgetIndex) + { + case WIDX_WATER_LEVEL: + _settings.waterLevel = value; + break; + } + + Invalidate(); + } + + void WaterPrepareDraw() + { + if (widgets != PageWidgets[WINDOW_MAPGEN_PAGE_WATER]) + { + widgets = PageWidgets[WINDOW_MAPGEN_PAGE_WATER]; + InitScrollWidgets(); + } + + SetCheckboxValue(WIDX_ADD_BEACHES, _settings.beaches != 0); + + SetPressedTab(); + } + + void WaterDraw(DrawPixelInfo& dpi) + { + DrawWidgets(dpi); + DrawTabImages(dpi); + + const auto textColour = colours[1]; - ft = Formatter(); - ft.Add(_waterLevel); DrawTextBasic( - dpi, - windowPos - + ScreenCoordsXY{ widgets[WIDX_HEIGHTMAP_WATER_LEVEL].left + 1, - widgets[WIDX_HEIGHTMAP_WATER_LEVEL].top + 1 }, - STR_COMMA16, ft, { labelColour }); + dpi, windowPos + ScreenCoordsXY{ 10, widgets[WIDX_WATER_LEVEL].top + 1 }, STR_WATER_LEVEL_LABEL, {}, + { textColour }); + + auto ft = Formatter(); + ft.Add(BaseZToMetres(_settings.waterLevel)); + DrawTextBasic( + dpi, windowPos + ScreenCoordsXY{ widgets[WIDX_WATER_LEVEL].left + 1, widgets[WIDX_WATER_LEVEL].top + 1 }, + STR_RIDE_LENGTH_ENTRY, ft, { colours[1] }); } #pragma endregion @@ -1275,7 +1396,7 @@ namespace OpenRCT2::Ui::Windows void OnClose() override { - MapGenUnloadHeightmap(); + MapGenUnloadHeightmapImage(); } void OnMouseUp(WidgetIndex widgetIndex) override @@ -1284,12 +1405,12 @@ namespace OpenRCT2::Ui::Windows { case WINDOW_MAPGEN_PAGE_BASE: return BaseMouseUp(widgetIndex); - case WINDOW_MAPGEN_PAGE_RANDOM: - return RandomMouseUp(widgetIndex); - case WINDOW_MAPGEN_PAGE_SIMPLEX: - return SimplexMouseUp(widgetIndex); - case WINDOW_MAPGEN_PAGE_HEIGHTMAP: - return HeightmapMouseUp(widgetIndex); + case WINDOW_MAPGEN_PAGE_FORESTS: + return ForestsMouseUp(widgetIndex); + case WINDOW_MAPGEN_PAGE_TERRAIN: + return TerrainMouseUp(widgetIndex); + case WINDOW_MAPGEN_PAGE_WATER: + return WaterMouseUp(widgetIndex); } } @@ -1299,10 +1420,12 @@ namespace OpenRCT2::Ui::Windows { case WINDOW_MAPGEN_PAGE_BASE: return BaseMouseDown(widgetIndex, &widgets[widgetIndex]); - case WINDOW_MAPGEN_PAGE_SIMPLEX: - return SimplexMouseDown(widgetIndex, &widgets[widgetIndex]); - case WINDOW_MAPGEN_PAGE_HEIGHTMAP: - return HeightmapMouseDown(widgetIndex, &widgets[widgetIndex]); + case WINDOW_MAPGEN_PAGE_TERRAIN: + return TerrainMouseDown(widgetIndex, &widgets[widgetIndex]); + case WINDOW_MAPGEN_PAGE_WATER: + return WaterMouseDown(widgetIndex, &widgets[widgetIndex]); + case WINDOW_MAPGEN_PAGE_FORESTS: + return ForestsMouseDown(widgetIndex, &widgets[widgetIndex]); } } @@ -1312,8 +1435,8 @@ namespace OpenRCT2::Ui::Windows { case WINDOW_MAPGEN_PAGE_BASE: return BaseDropdown(widgetIndex, selectedIndex); - case WINDOW_MAPGEN_PAGE_SIMPLEX: - return SimplexDropdown(widgetIndex, selectedIndex); + case WINDOW_MAPGEN_PAGE_TERRAIN: + return TerrainDropdown(widgetIndex, selectedIndex); } } @@ -1323,25 +1446,28 @@ namespace OpenRCT2::Ui::Windows { case WINDOW_MAPGEN_PAGE_BASE: return BaseUpdate(); - case WINDOW_MAPGEN_PAGE_RANDOM: - return RandomUpdate(); - case WINDOW_MAPGEN_PAGE_SIMPLEX: - return SimplexUpdate(); + case WINDOW_MAPGEN_PAGE_FORESTS: + return ForestsUpdate(); + case WINDOW_MAPGEN_PAGE_WATER: + return WaterUpdate(); } } void OnPrepareDraw() override { + bool isHeightMapImage = _settings.algorithm == MapGenAlgorithm::heightmapImage; + SetWidgetDisabled(WIDX_MAP_GENERATE, isHeightMapImage && !_heightmapLoaded); + switch (page) { case WINDOW_MAPGEN_PAGE_BASE: return BasePrepareDraw(); - case WINDOW_MAPGEN_PAGE_RANDOM: - return RandomPrepareDraw(); - case WINDOW_MAPGEN_PAGE_SIMPLEX: - return SimplexPrepareDraw(); - case WINDOW_MAPGEN_PAGE_HEIGHTMAP: - return HeightmapPrepareDraw(); + case WINDOW_MAPGEN_PAGE_FORESTS: + return ForestsPrepareDraw(); + case WINDOW_MAPGEN_PAGE_TERRAIN: + return TerrainPrepareDraw(); + case WINDOW_MAPGEN_PAGE_WATER: + return WaterPrepareDraw(); } } @@ -1351,23 +1477,57 @@ namespace OpenRCT2::Ui::Windows { case WINDOW_MAPGEN_PAGE_BASE: return BaseDraw(dpi); - case WINDOW_MAPGEN_PAGE_RANDOM: - return RandomDraw(dpi); - case WINDOW_MAPGEN_PAGE_SIMPLEX: - return SimplexDraw(dpi); - case WINDOW_MAPGEN_PAGE_HEIGHTMAP: - return HeightmapDraw(dpi); + case WINDOW_MAPGEN_PAGE_FORESTS: + return ForestsDraw(dpi); + case WINDOW_MAPGEN_PAGE_TERRAIN: + return TerrainDraw(dpi); + case WINDOW_MAPGEN_PAGE_WATER: + return WaterDraw(dpi); } } void OnTextInput(WidgetIndex widgetIndex, std::string_view text) override { + auto strText = std::string(text); + char* end; + + // Convert text to integer value + int32_t value{}; + if (page == WINDOW_MAPGEN_PAGE_BASE && widgetIndex == WIDX_SIMPLEX_BASE_FREQ) + value = 100 * strtof(strText.c_str(), &end); + else + value = strtol(strText.c_str(), &end, 10); + + if (*end != '\0') + return; + + // Take care of unit conversion + int32_t rawValue = value; + if (page != WINDOW_MAPGEN_PAGE_BASE) + { + switch (Config::Get().general.MeasurementFormat) + { + case MeasurementFormat::Imperial: + value = FeetToMetres(value); + [[fallthrough]]; + + default: + value = std::clamp(MetresToBaseZ(value), kMinimumLandHeight, kMaximumLandHeight); + break; + } + } + + // Pass on to the actual properties switch (page) { case WINDOW_MAPGEN_PAGE_BASE: - return TextInput(widgetIndex, text); - case WINDOW_MAPGEN_PAGE_SIMPLEX: - return TextInput(widgetIndex, text); + return BaseTextInput(widgetIndex, value); + case WINDOW_MAPGEN_PAGE_TERRAIN: + return TerrainTextInput(widgetIndex, value); + case WINDOW_MAPGEN_PAGE_WATER: + return WaterTextInput(widgetIndex, value); + case WINDOW_MAPGEN_PAGE_FORESTS: + return ForestsTextInput(widgetIndex, rawValue, value); } } @@ -1375,7 +1535,7 @@ namespace OpenRCT2::Ui::Windows { if (result == MODAL_RESULT_OK) { - if (!MapGenLoadHeightmap(path)) + if (!MapGenLoadHeightmapImage(path)) { // TODO: Display error popup return; @@ -1383,9 +1543,8 @@ namespace OpenRCT2::Ui::Windows // The window needs to be open while using the map _heightmapLoaded = true; - SetPage(WINDOW_MAPGEN_PAGE_HEIGHTMAP); - - HeightmapGenerateMap(); + _heightmapFilename = fs::u8path(path).filename().string(); + SetPage(WINDOW_MAPGEN_PAGE_BASE); } } diff --git a/src/openrct2-ui/windows/TopToolbar.cpp b/src/openrct2-ui/windows/TopToolbar.cpp index f33e30de8c..82b170f2e5 100644 --- a/src/openrct2-ui/windows/TopToolbar.cpp +++ b/src/openrct2-ui/windows/TopToolbar.cpp @@ -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 diff --git a/src/openrct2/localisation/StringIds.h b/src/openrct2/localisation/StringIds.h index 759052e089..807da1e1ba 100644 --- a/src/openrct2/localisation/StringIds.h +++ b/src/openrct2/localisation/StringIds.h @@ -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, diff --git a/src/openrct2/sprites.h b/src/openrct2/sprites.h index 243e2999e2..1c889f1432 100644 --- a/src/openrct2/sprites.h +++ b/src/openrct2/sprites.h @@ -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, diff --git a/src/openrct2/util/Util.cpp b/src/openrct2/util/Util.cpp index 3e1e8dd4da..fcaf6703a1 100644 --- a/src/openrct2/util/Util.cpp +++ b/src/openrct2/util/Util.cpp @@ -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 diff --git a/src/openrct2/util/Util.h b/src/openrct2/util/Util.h index ec4074c63a..b94e51042d 100644 --- a/src/openrct2/util/Util.h +++ b/src/openrct2/util/Util.h @@ -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) { diff --git a/src/openrct2/world/MapGen.cpp b/src/openrct2/world/MapGen.cpp index 42e5c3a2dd..fa61cecd59 100644 --- a/src/openrct2/world/MapGen.cpp +++ b/src/openrct2/world/MapGen.cpp @@ -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 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 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 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 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 grassTreeIds; std::vector desertTreeIds; @@ -335,16 +376,14 @@ static void MapGenPlaceTrees() } // Place trees - CoordsXY pos; - float treeToLandRatio = (10 + (UtilRand() % 30)) / 100.0f; + float treeToLandRatio = static_cast(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(y + 1), static_cast(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(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& 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(static_cast(value - minValue) / rangeIn * rangeOut) + settings->simplex_low; + value = static_cast(static_cast(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) diff --git a/src/openrct2/world/MapGen.h b/src/openrct2/world/MapGen.h index 158990c85a..05c7456ae8 100644 --- a/src/openrct2/world/MapGen.h +++ b/src/openrct2/world/MapGen.h @@ -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();