diff --git a/contributors.md b/contributors.md index 6b4b8312b7..3559a194ca 100644 --- a/contributors.md +++ b/contributors.md @@ -80,6 +80,7 @@ The following people are not part of the development team, but have been contrib * Christian Schubert (Osmodium) - Ensuring custom user content folders, incl. open folder. * (Xkeeper0) - Improved banner tooltips; multiplayer status in toolbar. * Kevin Strehl (bitman2049) - Tile inspector keybindings +* Anton Scharnowski (scrapes) - Added Scenery Scatter Options Window. ## Bug fixes * (halfbro) diff --git a/data/language/en-GB.txt b/data/language/en-GB.txt index df4f98a776..aa2c6fea61 100644 --- a/data/language/en-GB.txt +++ b/data/language/en-GB.txt @@ -3668,6 +3668,9 @@ STR_6346 :Tile Inspector: Decrease element height STR_6347 :Path additions cannot be placed on level crossings! STR_6348 :Remove level crossing first! STR_6349 :Random title sequence +STR_6350 :Scatter +STR_6351 :{BLACK}Size +STR_6352 :{BLACK}Amount ############# # Scenarios # diff --git a/src/openrct2-ui/WindowManager.cpp b/src/openrct2-ui/WindowManager.cpp index 8046deda62..7cdf60b6d6 100644 --- a/src/openrct2-ui/WindowManager.cpp +++ b/src/openrct2-ui/WindowManager.cpp @@ -101,6 +101,8 @@ public: return window_save_prompt_open(); case WC_SCENERY: return window_scenery_open(); + case WC_SCENERY_SCATTER: + return window_scenery_scatter_open(); #ifndef DISABLE_NETWORK case WC_SERVER_LIST: return window_server_list_open(); diff --git a/src/openrct2-ui/windows/Scenery.cpp b/src/openrct2-ui/windows/Scenery.cpp index 68721d280d..94fe09e08b 100644 --- a/src/openrct2-ui/windows/Scenery.cpp +++ b/src/openrct2-ui/windows/Scenery.cpp @@ -58,7 +58,10 @@ enum { uint16_t gWindowSceneryTabSelections[SCENERY_WINDOW_TABS]; uint8_t gWindowSceneryActiveTabIndex; -uint8_t gWindowSceneryClusterEnabled; +rct_window* gWindowSceneryScatterWindow; +uint16_t gWindowSceneryScatterSize; +uint16_t gWindowSceneryScatterAmount; +uint8_t gWindowSceneryScatterEnabled; uint8_t gWindowSceneryPaintEnabled; uint8_t gWindowSceneryRotation; colour_t gWindowSceneryPrimaryColour; @@ -486,7 +489,11 @@ rct_window* window_scenery_open() gSceneryPlaceRotation = 0; gWindowSceneryPaintEnabled = 0; // repaint coloured scenery tool state gWindowSceneryEyedropperEnabled = false; - gWindowSceneryClusterEnabled = 0; // build cluster tool state + gWindowSceneryScatterEnabled = 0; // build cluster tool state + gWindowSceneryScatterWindow = nullptr; + + gWindowSceneryScatterSize = 2; + gWindowSceneryScatterAmount = 10; window->min_width = WINDOW_SCENERY_WIDTH; window->max_width = WINDOW_SCENERY_WIDTH; @@ -506,6 +513,9 @@ void window_scenery_close(rct_window* w) hide_gridlines(); viewport_set_visibility(0); + if (gWindowSceneryScatterWindow != nullptr) + window_close(gWindowSceneryScatterWindow); + if (scenery_tool_is_active()) tool_cancel(); } @@ -572,6 +582,7 @@ static void window_scenery_mouseup(rct_window* w, rct_widgetindex widgetIndex) switch (widgetIndex) { case WIDX_SCENERY_CLOSE: + window_close(gWindowSceneryScatterWindow); window_close(w); break; case WIDX_SCENERY_ROTATE_OBJECTS_BUTTON: @@ -582,13 +593,13 @@ static void window_scenery_mouseup(rct_window* w, rct_widgetindex widgetIndex) break; case WIDX_SCENERY_REPAINT_SCENERY_BUTTON: gWindowSceneryPaintEnabled ^= 1; - gWindowSceneryClusterEnabled = 0; + gWindowSceneryScatterEnabled = 0; gWindowSceneryEyedropperEnabled = false; w->Invalidate(); break; case WIDX_SCENERY_EYEDROPPER_BUTTON: gWindowSceneryPaintEnabled = 0; - gWindowSceneryClusterEnabled = 0; + gWindowSceneryScatterEnabled = 0; gWindowSceneryEyedropperEnabled = !gWindowSceneryEyedropperEnabled; scenery_remove_ghost_tool_placement(); w->Invalidate(); @@ -596,15 +607,16 @@ static void window_scenery_mouseup(rct_window* w, rct_widgetindex widgetIndex) case WIDX_SCENERY_BUILD_CLUSTER_BUTTON: gWindowSceneryPaintEnabled = 0; gWindowSceneryEyedropperEnabled = false; - if (gWindowSceneryClusterEnabled == 1) + if (gWindowSceneryScatterEnabled == 1) { - gWindowSceneryClusterEnabled = 0; + gWindowSceneryScatterEnabled = 0; } else if ( network_get_mode() != NETWORK_MODE_CLIENT || network_can_perform_command(network_get_current_player_group_index(), -2)) { - gWindowSceneryClusterEnabled ^= 1; + gWindowSceneryScatterEnabled = 1; + gWindowSceneryScatterWindow = window_scenery_scatter_open(); } else { @@ -978,7 +990,7 @@ void window_scenery_invalidate(rct_window* w) w->pressed_widgets |= (1 << WIDX_SCENERY_REPAINT_SCENERY_BUTTON); if (gWindowSceneryEyedropperEnabled) w->pressed_widgets |= (1 << WIDX_SCENERY_EYEDROPPER_BUTTON); - if (gWindowSceneryClusterEnabled == 1) + if (gWindowSceneryScatterEnabled == 1) w->pressed_widgets |= (1ULL << WIDX_SCENERY_BUILD_CLUSTER_BUTTON); window_scenery_widgets[WIDX_SCENERY_ROTATE_OBJECTS_BUTTON].type = WWT_EMPTY; diff --git a/src/openrct2-ui/windows/SceneryScatter.cpp b/src/openrct2-ui/windows/SceneryScatter.cpp new file mode 100644 index 0000000000..ad298d2fc6 --- /dev/null +++ b/src/openrct2-ui/windows/SceneryScatter.cpp @@ -0,0 +1,258 @@ +/***************************************************************************** + * Copyright (c) 2014-2019 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 + +#define MAX_AMOUNT 100 + +enum WINDOW_CLEAR_SCENERY_WIDGET_IDX +{ + WIDX_BACKGROUND, + WIDX_TITLE, + WIDX_CLOSE, + WIDX_PREVIEW, + WIDX_DECREMENT, + WIDX_INCREMENT, + WIDX_AMOUNT, + WIDX_ADECREMENT, + WIDX_AINCREMENT +}; + +// clang-format off +static rct_widget window_scenery_scatter_widgets[] = { + { WWT_FRAME, 1, 0, 70, 0, 113, 0xFFFFFFFF, STR_NONE }, // panel / background + { WWT_CAPTION, 0, 0, 57, 1, 14, STR_SCENERY_SCATTER, STR_WINDOW_TITLE_TIP }, // title bar + { WWT_CLOSEBOX, 0, 57, 68, 2, 13, STR_CLOSE_X, STR_CLOSE_WINDOW_TIP }, // close x button + + { WWT_IMGBTN, 0, 5, 48, 17, 48, SPR_LAND_TOOL_SIZE_0, STR_NONE }, // preview box + { WWT_TRNBTN, 1, 6, 23, 18, 33, IMAGE_TYPE_REMAP | SPR_LAND_TOOL_DECREASE, STR_ADJUST_SMALLER_LAND_TIP }, // decrement size + { WWT_TRNBTN, 1, 32, 49, 32, 47, IMAGE_TYPE_REMAP | SPR_LAND_TOOL_INCREASE, STR_ADJUST_LARGER_LAND_TIP }, // increment size + + { WWT_IMGBTN, 0, 5, 48, 65, 96, 0xFFFFFFFF, STR_NONE }, // preview box + { WWT_TRNBTN, 1, 6, 23, 66, 81, IMAGE_TYPE_REMAP | SPR_LAND_TOOL_DECREASE, STR_ADJUST_SMALLER_LAND_TIP }, // decrement size amount + { WWT_TRNBTN, 1, 32, 49, 80, 94, IMAGE_TYPE_REMAP | SPR_LAND_TOOL_INCREASE, STR_ADJUST_LARGER_LAND_TIP }, // increment size amount + { WIDGETS_END }, +}; +// clang-format on + +static void window_scenery_scatter_close(rct_window* w); +static void window_scenery_scatter_mouseup(rct_window* w, rct_widgetindex widgetIndex); +static void window_scenery_scatter_mousedown(rct_window* w, rct_widgetindex widgetIndex, rct_widget* widget); +static void window_scenery_scatter_update(rct_window* w); +static void window_scenery_scatter_invalidate(rct_window* w); +static void window_scenery_scatter_paint(rct_window* w, rct_drawpixelinfo* dpi); +static void window_scenery_scatter_textinput(rct_window* w, rct_widgetindex widgetIndex, char* text); +static void window_scenery_scatter_inputsize(rct_window* w, rct_widgetindex widgetindex); + +// clang-format off +static rct_window_event_list window_clear_scenery_events = { + window_scenery_scatter_close, + window_scenery_scatter_mouseup, + nullptr, + window_scenery_scatter_mousedown, + nullptr, + nullptr, + window_scenery_scatter_update, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + window_scenery_scatter_textinput, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + window_scenery_scatter_invalidate, + window_scenery_scatter_paint, + nullptr +}; +// clang-format on + +rct_window* window_scenery_scatter_open() +{ + rct_window* window; + + // Check if window is already open + window = window_find_by_class(WC_SCENERY_SCATTER); + if (window != nullptr) + return window; + + window = window_create( + ScreenCoordsXY(context_get_width() - 70, 29), 70, 114, &window_clear_scenery_events, WC_SCENERY_SCATTER, 0); + window->widgets = window_scenery_scatter_widgets; + window->enabled_widgets = (1 << WIDX_CLOSE) | (1 << WIDX_INCREMENT) | (1 << WIDX_DECREMENT) | (1 << WIDX_PREVIEW) + | (1 << WIDX_AINCREMENT) | (1 << WIDX_ADECREMENT) | (1 << WIDX_AMOUNT); + window->hold_down_widgets = (1 << WIDX_INCREMENT) | (1 << WIDX_DECREMENT) | (1 << WIDX_AINCREMENT) | (1 << WIDX_ADECREMENT); + window_init_scroll_widgets(window); + window_push_others_below(window); + + window->colours[2] = COLOUR_DARK_GREEN; + + return window; +} + +static void window_scenery_scatter_close([[maybe_unused]] rct_window* w) +{ +} + +static void window_scenery_scatter_mouseup(rct_window* w, rct_widgetindex widgetIndex) +{ + switch (widgetIndex) + { + case WIDX_CLOSE: + gWindowSceneryScatterEnabled = 0; + window_close(w); + break; + case WIDX_PREVIEW: + case WIDX_AMOUNT: + window_scenery_scatter_inputsize(w, widgetIndex); + break; + } +} + +static void window_scenery_scatter_mousedown(rct_window* w, rct_widgetindex widgetIndex, [[maybe_unused]] rct_widget* widget) +{ + switch (widgetIndex) + { + case WIDX_DECREMENT: + // Decrement land tool size, if it stays within the limit + gWindowSceneryScatterSize = std::max(MINIMUM_TOOL_SIZE, gWindowSceneryScatterSize - 1); + + // Invalidate the window + w->Invalidate(); + break; + case WIDX_INCREMENT: + // Increment land tool size, if it stays within the limit + gWindowSceneryScatterSize = std::min(MAXIMUM_TOOL_SIZE, gWindowSceneryScatterSize + 1); + + // Invalidate the window + w->Invalidate(); + break; + + case WIDX_ADECREMENT: + // Decrement land tool size, if it stays within the limit + gWindowSceneryScatterAmount = std::max(1, gWindowSceneryScatterAmount - 1); + + // Invalidate the window + w->Invalidate(); + break; + case WIDX_AINCREMENT: + // Increment land tool size, if it stays within the limit + gWindowSceneryScatterAmount = std::min(MAX_AMOUNT, gWindowSceneryScatterAmount + 1); + + // Invalidate the window + w->Invalidate(); + break; + } +} + +static void window_scenery_scatter_textinput(rct_window* w, rct_widgetindex widgetIndex, char* text) +{ + int32_t size; + char* end; + + if (!(widgetIndex == WIDX_PREVIEW || widgetIndex == WIDX_AMOUNT) || text == nullptr) + return; + + size = strtol(text, &end, 10); + if (*end == '\0') + { + switch (widgetIndex) + { + case WIDX_PREVIEW: + size = std::max(MINIMUM_TOOL_SIZE, size); + size = std::min(MAXIMUM_TOOL_SIZE, size); + gWindowSceneryScatterSize = size; + break; + case WIDX_AMOUNT: + size = std::max(1, size); + size = std::min(MAX_AMOUNT, size); + gWindowSceneryScatterAmount = size; + break; + } + w->Invalidate(); + } +} + +static void window_scenery_scatter_inputsize(rct_window* w, rct_widgetindex widgetindex) +{ + uint8_t maxlen = 0; + switch (widgetindex) + { + case WIDX_PREVIEW: + TextInputDescriptionArgs[0] = MINIMUM_TOOL_SIZE; + TextInputDescriptionArgs[1] = MAXIMUM_TOOL_SIZE; + maxlen = 3; + break; + case WIDX_AMOUNT: + TextInputDescriptionArgs[0] = 1; + TextInputDescriptionArgs[1] = MAX_AMOUNT; + maxlen = 4; // Catch 100 + break; + } + window_text_input_open(w, widgetindex, STR_SELECTION_SIZE, STR_ENTER_SELECTION_SIZE, STR_NONE, STR_NONE, maxlen); +} + +static void window_scenery_scatter_update(rct_window* w) +{ + w->frame_no++; +} + +static void window_scenery_scatter_invalidate(rct_window* w) +{ + // Set the preview image button to be pressed down + w->pressed_widgets = (1 << WIDX_PREVIEW) | (1 << WIDX_AMOUNT); + // Update the preview image (for tool sizes up to 7) + window_scenery_scatter_widgets[WIDX_PREVIEW].image = land_tool_size_to_sprite_index(gWindowSceneryScatterSize); +} + +static void window_scenery_scatter_paint(rct_window* w, rct_drawpixelinfo* dpi) +{ + if (gWindowSceneryScatterEnabled == 0) + window_close(w); + + window_draw_widgets(w, dpi); + + int32_t x, y; + x = w->x + window_scenery_scatter_widgets[WIDX_PREVIEW].left; + y = w->y + window_scenery_scatter_widgets[WIDX_PREVIEW].bottom + 2; + gfx_draw_string_left(dpi, STR_SMALL_SIZE, nullptr, COLOUR_BLACK, x, y); + + x = w->x + window_scenery_scatter_widgets[WIDX_AMOUNT].left; + y = w->y + window_scenery_scatter_widgets[WIDX_AMOUNT].bottom + 2; + gfx_draw_string_left(dpi, STR_SMALL_AMOUNT, nullptr, COLOUR_BLACK, x, y); + + // Draw number for tool sizes bigger than 7 + + if (gWindowSceneryScatterSize > MAX_TOOL_SIZE_WITH_SPRITE) + { + x = w->x + (window_scenery_scatter_widgets[WIDX_PREVIEW].left + window_scenery_scatter_widgets[WIDX_PREVIEW].right) / 2; + y = w->y + (window_scenery_scatter_widgets[WIDX_PREVIEW].top + window_scenery_scatter_widgets[WIDX_PREVIEW].bottom) / 2; + gfx_draw_string_centred(dpi, STR_LAND_TOOL_SIZE_VALUE, x, y, COLOUR_BLACK, &gWindowSceneryScatterSize); + } + + x = w->x + (window_scenery_scatter_widgets[WIDX_AMOUNT].left + window_scenery_scatter_widgets[WIDX_AMOUNT].right) / 2; + y = w->y + (window_scenery_scatter_widgets[WIDX_AMOUNT].top + window_scenery_scatter_widgets[WIDX_AMOUNT].bottom) / 2; + gfx_draw_string_centred(dpi, STR_LAND_TOOL_SIZE_VALUE, x, y, COLOUR_BLACK, &gWindowSceneryScatterAmount); +} diff --git a/src/openrct2-ui/windows/TopToolbar.cpp b/src/openrct2-ui/windows/TopToolbar.cpp index be7350e056..2b11743396 100644 --- a/src/openrct2-ui/windows/TopToolbar.cpp +++ b/src/openrct2-ui/windows/TopToolbar.cpp @@ -1758,16 +1758,17 @@ static void window_top_toolbar_scenery_tool_down(int16_t x, int16_t y, rct_windo case SCENERY_TYPE_SMALL: { int32_t quantity = 1; - bool isCluster = gWindowSceneryClusterEnabled + bool isCluster = gWindowSceneryScatterEnabled && (network_get_mode() != NETWORK_MODE_CLIENT || network_can_perform_command(network_get_current_player_group_index(), -2)); if (isCluster) { - quantity = 35; + quantity = gWindowSceneryScatterAmount; } bool forceError = true; - for (int32_t q = 0; q < quantity; q++) + uint16_t retry = 0; + for (int32_t q = 0; q < quantity + retry; q++) { int32_t zCoordinate = gSceneryPlaceZ; rct_scenery_entry* scenery = get_small_scenery_entry((parameter_1 >> 8) & 0xFF); @@ -1783,8 +1784,16 @@ static void window_top_toolbar_scenery_tool_down(int16_t x, int16_t y, rct_windo parameter_2 |= util_rand() & 3; } - cur_grid_x += ((util_rand() % 16) - 8) * 32; - cur_grid_y += ((util_rand() % 16) - 8) * 32; + if (gWindowSceneryScatterSize % 2 != 0) + { + cur_grid_x += ((util_rand() % (gWindowSceneryScatterSize)) - (gWindowSceneryScatterSize / 2)) << 5; + cur_grid_y += ((util_rand() % (gWindowSceneryScatterSize)) - (gWindowSceneryScatterSize / 2)) << 5; + } + else + { + cur_grid_x += ((util_rand() % (gWindowSceneryScatterSize)) - (gWindowSceneryScatterSize / 2) + 1) << 5; + cur_grid_y += ((util_rand() % (gWindowSceneryScatterSize)) - (gWindowSceneryScatterSize / 2) + 1) << 5; + } if (!scenery_small_entry_has_flag(scenery, SMALL_SCENERY_FLAG_ROTATABLE)) { @@ -1850,6 +1859,11 @@ static void window_top_toolbar_scenery_tool_down(int16_t x, int16_t y, rct_windo break; } } + else + { + if (retry < gWindowSceneryScatterSize * gWindowSceneryScatterSize) + retry++; + } gSceneryPlaceZ = zCoordinate; } break; @@ -2680,12 +2694,23 @@ static void top_toolbar_tool_update_scenery(int16_t x, int16_t y) { case SCENERY_TYPE_SMALL: gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE; - if (gWindowSceneryClusterEnabled) + if (gWindowSceneryScatterEnabled) { - gMapSelectPositionA.x = mapTile.x - (8 << 5); - gMapSelectPositionA.y = mapTile.y - (8 << 5); - gMapSelectPositionB.x = mapTile.x + (7 << 5); - gMapSelectPositionB.y = mapTile.y + (7 << 5); + uint16_t cluster_size = (gWindowSceneryScatterSize - 1) << 5; + if (gWindowSceneryScatterSize % 2 != 0) + { + gMapSelectPositionA.x = mapTile.x - cluster_size / 2; + gMapSelectPositionA.y = mapTile.y - cluster_size / 2; + gMapSelectPositionB.x = mapTile.x + cluster_size / 2; + gMapSelectPositionB.y = mapTile.y + cluster_size / 2; + } + else + { + gMapSelectPositionA.x = mapTile.x - cluster_size / 2; + gMapSelectPositionA.y = mapTile.y - cluster_size / 2; + gMapSelectPositionB.x = mapTile.x + (cluster_size / 2) + (1 << 5); + gMapSelectPositionB.y = mapTile.y + (cluster_size / 2) + (1 << 5); + } } else { @@ -2698,7 +2723,7 @@ static void top_toolbar_tool_update_scenery(int16_t x, int16_t y) scenery = get_small_scenery_entry(selected_scenery); gMapSelectType = MAP_SELECT_TYPE_FULL; - if (!scenery_small_entry_has_flag(scenery, SMALL_SCENERY_FLAG_FULL_TILE) && !gWindowSceneryClusterEnabled) + if (!scenery_small_entry_has_flag(scenery, SMALL_SCENERY_FLAG_FULL_TILE) && !gWindowSceneryScatterEnabled) { gMapSelectType = MAP_SELECT_TYPE_QUARTER_0 + ((parameter2 & 0xFF) ^ 2); } diff --git a/src/openrct2-ui/windows/Window.h b/src/openrct2-ui/windows/Window.h index 620a692091..6a5c5c4cac 100644 --- a/src/openrct2-ui/windows/Window.h +++ b/src/openrct2-ui/windows/Window.h @@ -22,7 +22,10 @@ struct Vehicle; extern uint16_t gWindowSceneryTabSelections[]; extern uint8_t gWindowSceneryActiveTabIndex; -extern uint8_t gWindowSceneryClusterEnabled; +extern rct_window* gWindowSceneryScatterWindow; +extern uint16_t gWindowSceneryScatterSize; +extern uint16_t gWindowSceneryScatterAmount; +extern uint8_t gWindowSceneryScatterEnabled; extern uint8_t gWindowSceneryPaintEnabled; extern uint8_t gWindowSceneryRotation; extern colour_t gWindowSceneryPrimaryColour; @@ -181,3 +184,5 @@ void window_tooltip_reset(ScreenCoordsXY screenCoords); void window_tooltip_show(rct_string_id id, ScreenCoordsXY screenCoords); void window_tooltip_open(rct_window* widgetWindow, rct_widgetindex widgetIndex, ScreenCoordsXY screenCoords); void window_tooltip_close(); + +rct_window* window_scenery_scatter_open(); diff --git a/src/openrct2/interface/Window.h b/src/openrct2/interface/Window.h index c6ba5e265e..4f7fdeb4a8 100644 --- a/src/openrct2/interface/Window.h +++ b/src/openrct2/interface/Window.h @@ -410,6 +410,7 @@ enum WC_TRACK_DELETE_PROMPT = 48, WC_INSTALL_TRACK = 49, WC_CLEAR_SCENERY = 50, + WC_SCENERY_SCATTER = 51, WC_NOTIFICATION_OPTIONS = 109, WC_CHEATS = 110, WC_RESEARCH = 111, diff --git a/src/openrct2/localisation/StringIds.h b/src/openrct2/localisation/StringIds.h index 7a22c49185..4225b79419 100644 --- a/src/openrct2/localisation/StringIds.h +++ b/src/openrct2/localisation/StringIds.h @@ -3896,6 +3896,10 @@ enum STR_OPTIONS_RANDOM_TITLE_SEQUENCE = 6349, + STR_SCENERY_SCATTER = 6350, + STR_SMALL_SIZE = 6351, + STR_SMALL_AMOUNT = 6352, + // Have to include resource strings (from scenarios and objects) for the time being now that language is partially working STR_COUNT = 32768 };