/***************************************************************************** * Copyright (c) 2014-2024 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 * * OpenRCT2 is licensed under the GNU General Public License version 3. *****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace OpenRCT2::Ui::Windows { enum { WINDOW_MAPGEN_PAGE_BASE, WINDOW_MAPGEN_PAGE_TERRAIN, WINDOW_MAPGEN_PAGE_WATER, WINDOW_MAPGEN_PAGE_FORESTS, WINDOW_MAPGEN_PAGE_COUNT }; enum { WIDX_BACKGROUND, WIDX_TITLE, WIDX_CLOSE, WIDX_PAGE_BACKGROUND, WIDX_TAB_1, WIDX_TAB_2, WIDX_TAB_3, WIDX_TAB_4, WIDX_MAP_GENERATE, TAB_BEGIN, 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_HEIGHTMAP_SOURCE, WIDX_HEIGHTMAP_SOURCE_DROPDOWN, 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_HEIGHTMAP_GROUP, WIDX_HEIGHTMAP_SELECT, WIDX_HEIGHTMAP_NORMALIZE, WIDX_HEIGHTMAP_SMOOTH_HEIGHTMAP, WIDX_HEIGHTMAP_STRENGTH, WIDX_HEIGHTMAP_STRENGTH_UP, WIDX_HEIGHTMAP_STRENGTH_DOWN, 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_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 int32_t WW = 300; static constexpr int32_t WH = 220; #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 */ \ MakeWidget({ 185, 200 }, { 109, 14 }, WindowWidgetType::Button, WindowColour::Secondary, STR_MAPGEN_ACTION_GENERATE) // clang-format off 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, 70}, WindowWidgetType::Groupbox, WindowColour::Secondary, STR_MAPGEN_SELECT_HEIGHTMAP), // WIDX_HEIGHTMAP_GROUP MakeWidget ({179, 107}, {109, 14}, WindowWidgetType::Button, WindowColour::Secondary, STR_MAPGEN_SELECT_HEIGHTMAP), // WIDX_HEIGHTMAP_SELECT MakeWidget ({ 10, 107}, {150, 12}, WindowWidgetType::Checkbox, WindowColour::Secondary, STR_MAPGEN_NORMALIZE ), // WIDX_HEIGHTMAP_NORMALIZE MakeWidget ({ 10, 125}, {150, 12}, WindowWidgetType::Checkbox, WindowColour::Secondary, STR_MAPGEN_SMOOTH_HEIGHTMAP), // WIDX_HEIGHTMAP_SMOOTH_HEIGHTMAP MakeSpinnerWidgets({179, 141}, {109, 12}, WindowWidgetType::Spinner, WindowColour::Secondary ), // WIDX_HEIGHTMAP_STRENGTH{,_UP,_DOWN} kWidgetsEnd, }; 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 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 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, }; static Widget* PageWidgets[WINDOW_MAPGEN_PAGE_COUNT] = { BaseWidgets, TerrainWidgets, WaterWidgets, ForestsWidgets, }; // clang-format on #pragma endregion #pragma region Widget flags // clang-format off static uint64_t PageDisabledWidgets[WINDOW_MAPGEN_PAGE_COUNT] = { (1uLL << WIDX_HEIGHTMAP_SMOOTH_HEIGHTMAP) | (1uLL << WIDX_HEIGHTMAP_STRENGTH) | (1uLL << WIDX_HEIGHTMAP_STRENGTH_UP) | (1uLL << WIDX_HEIGHTMAP_STRENGTH_DOWN) | (1uLL << WIDX_HEIGHTMAP_NORMALIZE), 0, 0, 0 }; static uint64_t HoldDownWidgets[WINDOW_MAPGEN_PAGE_COUNT] = { (1uLL << WIDX_MAP_SIZE_Y_UP) | (1uLL << WIDX_MAP_SIZE_Y_DOWN) | (1uLL << WIDX_MAP_SIZE_X_UP) | (1uLL << WIDX_MAP_SIZE_X_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_HEIGHTMAP_STRENGTH_UP) | (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_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, }; // clang-format on #pragma endregion // clang-format off static constexpr int32_t TabAnimationDivisor[WINDOW_MAPGEN_PAGE_COUNT] = { 2, 1, 1, 1, }; static constexpr int32_t TabAnimationFrames[WINDOW_MAPGEN_PAGE_COUNT] = { 4, 1, 1, 1, }; static constexpr int32_t TabAnimationLoops[WINDOW_MAPGEN_PAGE_COUNT] = { 16, 1, 1, 1, }; // clang-format on enum class ResizeDirection { Both, X, Y, }; static void HeightmapLoadsaveCallback(int32_t result, const utf8* path); class MapGenWindow final : public Window { private: ResizeDirection _resizeDirection{ ResizeDirection::Both }; bool _mapWidthAndHeightLinked{ true }; MapGenSettings _settings{}; bool _randomTerrain = true; bool _heightmapLoaded = false; void SetPage(int32_t newPage) { page = newPage; frame_no = 0; RemoveViewport(); hold_down_widgets = HoldDownWidgets[newPage]; widgets = PageWidgets[newPage]; disabled_widgets = PageDisabledWidgets[newPage]; pressed_widgets = PressedWidgets[newPage]; // Enable heightmap widgets if one is loaded if (_settings.algorithm == MapGenAlgorithm::heightmapImage && _heightmapLoaded) { SetWidgetEnabled(WIDX_HEIGHTMAP_NORMALIZE, true); SetWidgetEnabled(WIDX_HEIGHTMAP_SMOOTH_HEIGHTMAP, true); 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); } InitScrollWidgets(); Invalidate(); } void SetPressedTab() { for (auto i = 0; i < WINDOW_MAPGEN_PAGE_COUNT; i++) pressed_widgets &= ~(1 << (WIDX_TAB_1 + i)); pressed_widgets |= 1LL << (WIDX_TAB_1 + page); } void DrawTabImage(DrawPixelInfo& dpi, int32_t newPage, int32_t spriteIndex) { WidgetIndex widgetIndex = WIDX_TAB_1 + newPage; if (!WidgetIsDisabled(*this, widgetIndex)) { if (page == newPage) { int32_t frame = frame_no / TabAnimationDivisor[page]; spriteIndex += (frame % TabAnimationFrames[page]); } GfxDrawSprite( dpi, ImageId(spriteIndex), windowPos + ScreenCoordsXY{ widgets[widgetIndex].left, widgets[widgetIndex].top }); } } void DrawTabImages(DrawPixelInfo& dpi) { 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) { if (_mapWidthAndHeightLinked) _resizeDirection = ResizeDirection::Both; if (_resizeDirection != ResizeDirection::X) { _settings.mapSize.y = std::clamp( _settings.mapSize.y + sizeOffset, kMinimumMapSizeTechnical, kMaximumMapSizeTechnical); } if (_resizeDirection != ResizeDirection::Y) { _settings.mapSize.x = std::clamp( _settings.mapSize.x + sizeOffset, kMinimumMapSizeTechnical, kMaximumMapSizeTechnical); } } void InputMapSize(WidgetIndex callingWidget, int32_t currentValue) { Formatter ft; ft.Add(kMinimumMapSizePractical); ft.Add(kMaximumMapSizePractical); // Practical map size is 2 lower than the technical map size currentValue -= 2; WindowTextInputOpen( this, callingWidget, STR_MAP_SIZE_2, STR_ENTER_MAP_SIZE, ft, STR_FORMAT_INTEGER, currentValue, 4); } void SharedMouseUp(WidgetIndex widgetIndex) { switch (widgetIndex) { case WIDX_CLOSE: Close(); break; case WIDX_TAB_1: case WIDX_TAB_2: case WIDX_TAB_3: 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); if (_settings.algorithm == MapGenAlgorithm::simplexNoise) SimplexMouseUp(widgetIndex); else if (_settings.algorithm == MapGenAlgorithm::heightmapImage) HeightmapMouseUp(widgetIndex); switch (widgetIndex) { case WIDX_MAP_SIZE_Y: _resizeDirection = ResizeDirection::Y; InputMapSize(WIDX_MAP_SIZE_Y, _settings.mapSize.y); break; case WIDX_MAP_SIZE_X: _resizeDirection = ResizeDirection::X; InputMapSize(WIDX_MAP_SIZE_X, _settings.mapSize.x); break; case WIDX_MAP_SIZE_LINK: _mapWidthAndHeightLinked = !_mapWidthAndHeightLinked; 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: _resizeDirection = ResizeDirection::Y; ChangeMapSize(+1); Invalidate(); break; case WIDX_MAP_SIZE_Y_DOWN: _resizeDirection = ResizeDirection::Y; ChangeMapSize(-1); Invalidate(); break; case WIDX_MAP_SIZE_X_UP: _resizeDirection = ResizeDirection::X; ChangeMapSize(+1); Invalidate(); break; case WIDX_MAP_SIZE_X_DOWN: _resizeDirection = ResizeDirection::X; ChangeMapSize(-1); Invalidate(); break; case WIDX_HEIGHTMAP_SOURCE_DROPDOWN: { using namespace Dropdown; constexpr ItemExt items[] = { ToggleOption(0, STR_HEIGHTMAP_FLATLAND), ToggleOption(1, STR_HEIGHTMAP_SIMPLEX_NOISE), ToggleOption(2, STR_HEIGHTMAP_FILE), }; SetItems(items); 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); SetChecked(EnumValue(_settings.algorithm), true); break; } } } void BaseUpdate() { // Tab animation if (++frame_no >= TabAnimationLoops[page]) frame_no = 0; InvalidateWidget(WIDX_TAB_1); } void BaseDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) { 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); switch (widgetIndex) { case WIDX_MAP_SIZE_Y: case WIDX_MAP_SIZE_X: // The practical size is 2 lower than the technical size value += 2; if (_resizeDirection == ResizeDirection::Y || _mapWidthAndHeightLinked) _settings.mapSize.y = value; if (_resizeDirection == ResizeDirection::X || _mapWidthAndHeightLinked) _settings.mapSize.x = value; break; } Invalidate(); } void BasePrepareDraw() { if (widgets != PageWidgets[WINDOW_MAPGEN_PAGE_BASE]) { widgets = PageWidgets[WINDOW_MAPGEN_PAGE_BASE]; InitScrollWidgets(); } // Only allow linking the map size when X and Y are the same SetWidgetPressed(WIDX_MAP_SIZE_LINK, _mapWidthAndHeightLinked); SetWidgetDisabled(WIDX_MAP_SIZE_LINK, _settings.mapSize.x != _settings.mapSize.y); 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(_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_SELECT].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 textColour = colours[1]; DrawTextBasic( dpi, windowPos + ScreenCoordsXY{ 10, widgets[WIDX_MAP_SIZE_Y].top + 1 }, STR_MAP_SIZE, {}, { textColour }); 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; 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(); } void ForestsDraw(DrawPixelInfo& dpi) { DrawWidgets(dpi); DrawTabImages(dpi); const auto textColour = colours[1]; // 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 DrawTextBasic( dpi, windowPos + ScreenCoordsXY{ 10, widgets[WIDX_TREE_ALTITUDE_MAX].top + 1 }, STR_MAPGEN_TREE_MAX_ALTITUDE, {}, { textColour }); 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, { textColour }); } #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_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: _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; } } 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 auto strengthColour = _settings.smooth_height_map ? enabledColour : disabledColour; DrawTextBasic( dpi, windowPos + ScreenCoordsXY{ 24, widgets[WIDX_HEIGHTMAP_STRENGTH].top + 1 }, STR_MAPGEN_SMOOTH_STRENGTH, {}, { strengthColour }); auto ft = Formatter(); ft.Add(_settings.smooth_strength); DrawTextBasic( dpi, windowPos + ScreenCoordsXY{ widgets[WIDX_HEIGHTMAP_STRENGTH].left + 1, widgets[WIDX_HEIGHTMAP_STRENGTH].top + 1 }, STR_COMMA16, ft, { strengthColour }); } #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) { const auto& widget = widgets[widgetIndex]; ScreenCoordsXY pos = { windowPos.x + widget.left, windowPos.y + widget.top }; if (IsWidgetDisabled(widgetIndex)) { // Draw greyed out (light border bottom right shadow) auto colour = colours[widget.colour].colour; colour = ColourMapA[colour].lighter; GfxDrawSpriteSolid(dpi, image, pos + ScreenCoordsXY{ 1, 1 }, colour); // Draw greyed out (dark) colour = colours[widget.colour].colour; colour = ColourMapA[colour].mid_light; GfxDrawSpriteSolid(dpi, image, pos, colour); } else { GfxDrawSprite(dpi, image, pos); } } void DrawDropdownButtons(DrawPixelInfo& dpi, WidgetIndex floorWidgetIndex, WidgetIndex edgeWidgetIndex) { auto& objManager = GetContext()->GetObjectManager(); const auto surfaceObj = static_cast( objManager.GetLoadedObject(ObjectType::TerrainSurface, _settings.landTexture)); ImageId surfaceImage; if (surfaceObj != nullptr) { surfaceImage = ImageId(surfaceObj->IconImageId); if (surfaceObj->Colour != TerrainSurfaceObject::kNoValue) { surfaceImage = surfaceImage.WithPrimary(surfaceObj->Colour); } } ImageId edgeImage; const auto edgeObj = static_cast( objManager.GetLoadedObject(ObjectType::TerrainEdge, _settings.edgeTexture)); if (edgeObj != nullptr) { edgeImage = ImageId(edgeObj->IconImageId); } DrawDropdownButton(dpi, floorWidgetIndex, surfaceImage); DrawDropdownButton(dpi, edgeWidgetIndex, edgeImage); } 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 if (!_randomTerrain) { SetWidgetEnabled(WIDX_FLOOR_TEXTURE, true); SetWidgetEnabled(WIDX_WALL_TEXTURE, true); } else { SetWidgetEnabled(WIDX_FLOOR_TEXTURE, false); SetWidgetEnabled(WIDX_WALL_TEXTURE, false); } SetPressedTab(); } void TerrainDraw(DrawPixelInfo& dpi) { DrawWidgets(dpi); DrawTabImages(dpi); DrawDropdownButtons(dpi, WIDX_FLOOR_TEXTURE, WIDX_WALL_TEXTURE); const auto textColour = colours[1]; // Floor texture label DrawTextBasic( dpi, windowPos + ScreenCoordsXY{ 10, widgets[WIDX_FLOOR_TEXTURE].top + 1 }, STR_TERRAIN_LABEL, {}, { textColour }); // Minimum land height label and value DrawTextBasic( dpi, windowPos + ScreenCoordsXY{ 10, widgets[WIDX_HEIGHTMAP_LOW].top + 1 }, STR_MAPGEN_MIN_LAND_HEIGHT, {}, { textColour }); auto ft = Formatter(); ft.Add(BaseZToMetres(_settings.heightmapLow)); DrawTextBasic( dpi, windowPos + ScreenCoordsXY{ widgets[WIDX_HEIGHTMAP_LOW].left + 1, widgets[WIDX_HEIGHTMAP_LOW].top + 1 }, STR_RIDE_LENGTH_ENTRY, ft, { textColour }); // Maximum land height label and value DrawTextBasic( dpi, windowPos + ScreenCoordsXY{ 10, widgets[WIDX_HEIGHTMAP_HIGH].top + 1 }, STR_MAPGEN_MAX_LAND_HEIGHT, {}, { textColour }); ft = Formatter(); ft.Add(BaseZToMetres(_settings.heightmapHigh)); DrawTextBasic( dpi, windowPos + ScreenCoordsXY{ widgets[WIDX_HEIGHTMAP_HIGH].left + 1, widgets[WIDX_HEIGHTMAP_HIGH].top + 1 }, STR_RIDE_LENGTH_ENTRY, ft, { textColour }); } #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]; DrawTextBasic( 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 public: void OnOpen() override { number = 0; frame_no = 0; page = WINDOW_MAPGEN_PAGE_BASE; Invalidate(); widgets = PageWidgets[WINDOW_MAPGEN_PAGE_BASE]; hold_down_widgets = HoldDownWidgets[WINDOW_MAPGEN_PAGE_BASE]; pressed_widgets = PressedWidgets[WINDOW_MAPGEN_PAGE_BASE]; disabled_widgets = PageDisabledWidgets[WINDOW_MAPGEN_PAGE_BASE]; InitScrollWidgets(); _heightmapLoaded = false; } void OnClose() override { MapGenUnloadHeightmapImage(); } void OnMouseUp(WidgetIndex widgetIndex) override { switch (page) { case WINDOW_MAPGEN_PAGE_BASE: return BaseMouseUp(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); } } void OnMouseDown(WidgetIndex widgetIndex) override { switch (page) { case WINDOW_MAPGEN_PAGE_BASE: return BaseMouseDown(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]); } } void OnDropdown(WidgetIndex widgetIndex, int32_t selectedIndex) override { switch (page) { case WINDOW_MAPGEN_PAGE_BASE: return BaseDropdown(widgetIndex, selectedIndex); case WINDOW_MAPGEN_PAGE_TERRAIN: return TerrainDropdown(widgetIndex, selectedIndex); } } void OnUpdate() override { switch (page) { case WINDOW_MAPGEN_PAGE_BASE: return BaseUpdate(); 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_FORESTS: return ForestsPrepareDraw(); case WINDOW_MAPGEN_PAGE_TERRAIN: return TerrainPrepareDraw(); case WINDOW_MAPGEN_PAGE_WATER: return WaterPrepareDraw(); } } void OnDraw(DrawPixelInfo& dpi) override { switch (page) { case WINDOW_MAPGEN_PAGE_BASE: return BaseDraw(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 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); } } void AfterLoadingHeightMap(int32_t result, const utf8* path) { if (result == MODAL_RESULT_OK) { if (!MapGenLoadHeightmapImage(path)) { // TODO: Display error popup return; } // The window needs to be open while using the map _heightmapLoaded = true; SetPage(WINDOW_MAPGEN_PAGE_BASE); } } void OnResize() override { ResizeFrameWithPage(); } }; WindowBase* MapgenOpen() { return WindowFocusOrCreate(WindowClass::Mapgen, WW, WH, WF_10 | WF_AUTO_POSITION | WF_CENTRE_SCREEN); } static void HeightmapLoadsaveCallback(int32_t result, const utf8* path) { auto* w = static_cast(MapgenOpen()); w->AfterLoadingHeightMap(result, path); } } // namespace OpenRCT2::Ui::Windows