From 92fc010b9af005d612d3282c9a9e739dcac17f09 Mon Sep 17 00:00:00 2001 From: Robert Jordan Date: Sun, 31 Dec 2017 06:42:40 -0500 Subject: [PATCH] Feature: "Load Scenario" title sequence command New command goes by LOADSC in script files and in the enumeration. Scenarios are stored using the internal also used for localisation. Scenarios selected can only be scenarios to originally come with one of the games or expansions. Modified Scenario Select window to have a mode just for title editor scenario selection. --- data/language/en-GB.txt | 8 +++ distribution/changelog.txt | 2 + src/openrct2-ui/WindowManager.cpp | 2 +- src/openrct2-ui/windows/ServerStart.cpp | 2 +- .../windows/TitleCommandEditor.cpp | 69 ++++++++++++++++++- src/openrct2-ui/windows/TitleEditor.cpp | 21 ++++++ src/openrct2-ui/windows/TitleMenu.cpp | 2 +- .../windows/TitleScenarioSelect.cpp | 63 +++++++++++------ src/openrct2-ui/windows/Window.h | 2 +- src/openrct2/localisation/string_ids.h | 15 +++- src/openrct2/rct1/S4Importer.cpp | 2 + src/openrct2/scenario/ScenarioRepository.cpp | 22 +++++- src/openrct2/scenario/ScenarioRepository.h | 9 ++- src/openrct2/scenario/ScenarioSources.cpp | 2 +- src/openrct2/title/TitleSequence.cpp | 28 +++++++- src/openrct2/title/TitleSequence.h | 4 ++ src/openrct2/title/TitleSequencePlayer.cpp | 15 ++++ 17 files changed, 232 insertions(+), 36 deletions(-) diff --git a/data/language/en-GB.txt b/data/language/en-GB.txt index 301a0bb681..66a4d597ad 100644 --- a/data/language/en-GB.txt +++ b/data/language/en-GB.txt @@ -4480,6 +4480,14 @@ STR_6170 :Tweaks STR_6171 :Search STR_6172 :{SMALLFONT}{BLACK}Search STR_6173 :Please provide the name to search: +STR_6174 :Load Save +STR_6175 :Load Scenario +STR_6176 :Scenario to load: +STR_6177 :Load{MOVE_X}{87}No scenario selected +STR_6178 :Load{MOVE_X}{87}{RED}Missing scenario +STR_6179 :Select +STR_6180 :No scenario selected +STR_6181 :{RED}Missing scenario ############# # Scenarios # diff --git a/distribution/changelog.txt b/distribution/changelog.txt index 15ec5dfbb6..92351057e5 100644 --- a/distribution/changelog.txt +++ b/distribution/changelog.txt @@ -30,6 +30,8 @@ - Feature: Allow using object files from RCT Classic. - Feature: Title sequences now testable in-game. - Feature: Vehicles with matching capabilities are now always switchable. +- Feature: Add search box to track design window. +- Feature: Add load scenario command to title sequences. - Fix: [#816] In the map window, there are more peeps flickering than there are selected (original bug). - Fix: [#996, #2589, #2875] Viewport scrolling no longer shakes or gets stuck. - Fix: [#1185] Close button colour of prompt windows does not match. diff --git a/src/openrct2-ui/WindowManager.cpp b/src/openrct2-ui/WindowManager.cpp index 5638b0b5c6..b3f082c06c 100644 --- a/src/openrct2-ui/WindowManager.cpp +++ b/src/openrct2-ui/WindowManager.cpp @@ -247,7 +247,7 @@ public: return window_track_list_open(rideItem); } case WC_SCENARIO_SELECT: - return window_scenarioselect_open((scenarioselect_callback) intent->GetPointerExtra(INTENT_EXTRA_CALLBACK)); + return window_scenarioselect_open((scenarioselect_callback) intent->GetPointerExtra(INTENT_EXTRA_CALLBACK), false); case WD_VEHICLE: return window_ride_open_vehicle((rct_vehicle *) intent->GetPointerExtra(INTENT_EXTRA_VEHICLE)); case WD_TRACK: diff --git a/src/openrct2-ui/windows/ServerStart.cpp b/src/openrct2-ui/windows/ServerStart.cpp index 2482334d70..6f4a98be00 100644 --- a/src/openrct2-ui/windows/ServerStart.cpp +++ b/src/openrct2-ui/windows/ServerStart.cpp @@ -221,7 +221,7 @@ static void window_server_start_mouseup(rct_window *w, rct_widgetindex widgetInd window_invalidate(w); break; case WIDX_START_SERVER: - window_scenarioselect_open(window_server_start_scenarioselect_callback); + window_scenarioselect_open(window_server_start_scenarioselect_callback, false); break; case WIDX_LOAD_SERVER: network_set_password(_password); diff --git a/src/openrct2-ui/windows/TitleCommandEditor.cpp b/src/openrct2-ui/windows/TitleCommandEditor.cpp index 5f99b64ee4..cc4d60b5a8 100644 --- a/src/openrct2-ui/windows/TitleCommandEditor.cpp +++ b/src/openrct2-ui/windows/TitleCommandEditor.cpp @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include #include #include @@ -36,7 +38,8 @@ typedef struct TITLE_COMMAND_ORDER { } TITLE_COMMAND_ORDER; static TITLE_COMMAND_ORDER _window_title_command_editor_orders[] = { - { TITLE_SCRIPT_LOAD, STR_TITLE_EDITOR_ACTION_LOAD, STR_TITLE_EDITOR_ARGUMENT_SAVEFILE }, + { TITLE_SCRIPT_LOAD, STR_TITLE_EDITOR_ACTION_LOAD_SAVE, STR_TITLE_EDITOR_ARGUMENT_SAVEFILE }, + { TITLE_SCRIPT_LOADSC, STR_TITLE_EDITOR_ACTION_LOAD_SCENARIO, STR_TITLE_EDITOR_ARGUMENT_SCENARIO }, { TITLE_SCRIPT_LOCATION, STR_TITLE_EDITOR_COMMAND_TYPE_LOCATION, STR_TITLE_EDITOR_ARGUMENT_COORDINATES }, { TITLE_SCRIPT_ROTATE, STR_TITLE_EDITOR_COMMAND_TYPE_ROTATE, STR_TITLE_EDITOR_ARGUMENT_ROTATIONS }, { TITLE_SCRIPT_ZOOM, STR_TITLE_EDITOR_COMMAND_TYPE_ZOOM, STR_TITLE_EDITOR_ARGUMENT_ZOOM_LEVEL }, @@ -46,7 +49,7 @@ static TITLE_COMMAND_ORDER _window_title_command_editor_orders[] = { { TITLE_SCRIPT_END, STR_TITLE_EDITOR_END, STR_NONE }, }; -#define NUM_COMMANDS 8 +#define NUM_COMMANDS 9 enum WINDOW_WATER_WIDGET_IDX { WIDX_BACKGROUND, @@ -60,6 +63,7 @@ enum WINDOW_WATER_WIDGET_IDX { WIDX_INPUT, WIDX_INPUT_DROPDOWN, WIDX_GET, + WIDX_SELECT, WIDX_OKAY, WIDX_CANCEL }; @@ -94,6 +98,7 @@ static rct_widget window_title_command_editor_widgets[] = { { WWT_DROPDOWN_BUTTON, 1, WW-28, WW-18, BY2+1, BY2+10, STR_DROPDOWN_GLYPH, STR_NONE }, { WWT_DROPDOWN_BUTTON, 1, WS+WHA+3, WW-WS-1, BY2-14, BY2-3, STR_TITLE_COMMAND_EDITOR_ACTION_GET_LOCATION, STR_NONE }, // Get location/zoom/etc + { WWT_DROPDOWN_BUTTON, 1, WS+WHA+12, WW-WS-1, BY2-14, BY2-3, STR_TITLE_COMMAND_EDITOR_ACTION_SELECT_SCENARIO, STR_NONE }, // Select scenario { WWT_DROPDOWN_BUTTON, 1, 10, 80, WH-21, WH-10, STR_OK, STR_NONE }, // OKAY { WWT_DROPDOWN_BUTTON, 1, WW-80, WW-10, WH-21, WH-10, STR_CANCEL, STR_NONE }, // Cancel @@ -109,6 +114,7 @@ static void window_title_command_editor_invalidate(rct_window * w); static void window_title_command_editor_paint(rct_window * w, rct_drawpixelinfo * dpi); static void window_title_command_editor_textinput(rct_window * w, rct_widgetindex widgetIndex, char * text); static void window_title_command_editor_inputsize(rct_window * w); +static void scenario_select_callback(const utf8 * path); static sint32 get_command_info_index(sint32 index); static TITLE_COMMAND_ORDER get_command_info(sint32 index); static LocationXY16 get_location(); @@ -145,6 +151,16 @@ static rct_window_event_list window_title_command_editor_events = { nullptr }; +static void scenario_select_callback(const utf8 * path) +{ + if (command.Type == TITLE_SCRIPT_LOADSC) + { + const utf8 * fileName = path_get_filename(path); + auto scenario = GetScenarioRepository()->GetByFilename(fileName); + safe_strcpy(command.Scenario, scenario->internal_name, sizeof(command.Scenario)); + } +} + static sint32 get_command_info_index(sint32 index) { for (sint32 i = 0; i < NUM_COMMANDS; i++) @@ -225,6 +241,7 @@ void window_title_command_editor_open(TitleSequence * sequence, sint32 index, bo (1 << WIDX_INPUT) | (1 << WIDX_INPUT_DROPDOWN) | (1 << WIDX_GET) | + (1 << WIDX_SELECT) | (1 << WIDX_OKAY) | (1 << WIDX_CANCEL); window_init_scroll_widgets(window); @@ -298,6 +315,9 @@ static void window_title_command_editor_mouseup(rct_window * w, rct_widgetindex } window_invalidate(w); break; + case WIDX_SELECT: + window_scenarioselect_open(scenario_select_callback, true); + break; case WIDX_OKAY: if (_window_title_command_editor_insert) { @@ -446,6 +466,8 @@ static void window_title_command_editor_dropdown(rct_window * w, rct_widgetindex command.SaveIndex = 0xFF; } break; + case TITLE_SCRIPT_LOADSC: + command.Scenario[0] = '\0'; } window_invalidate(w); break; @@ -561,6 +583,7 @@ static void window_title_command_editor_invalidate(rct_window * w) window_title_command_editor_widgets[WIDX_INPUT].type = WWT_EMPTY; window_title_command_editor_widgets[WIDX_INPUT_DROPDOWN].type = WWT_EMPTY; window_title_command_editor_widgets[WIDX_GET].type = WWT_EMPTY; + window_title_command_editor_widgets[WIDX_SELECT].type = WWT_EMPTY; switch (command.Type) { case TITLE_SCRIPT_LOAD: @@ -568,6 +591,10 @@ static void window_title_command_editor_invalidate(rct_window * w) window_title_command_editor_widgets[WIDX_INPUT].type = WWT_DROPDOWN; window_title_command_editor_widgets[WIDX_INPUT_DROPDOWN].type = WWT_DROPDOWN_BUTTON; break; + case TITLE_SCRIPT_LOADSC: + window_title_command_editor_widgets[WIDX_INPUT].type = WWT_DROPDOWN; + window_title_command_editor_widgets[WIDX_SELECT].type = WWT_DROPDOWN_BUTTON; + break; case TITLE_SCRIPT_LOCATION: window_title_command_editor_widgets[WIDX_TEXTBOX_X].type = WWT_TEXT_BOX; window_title_command_editor_widgets[WIDX_TEXTBOX_Y].type = WWT_TEXT_BOX; @@ -642,4 +669,42 @@ static void window_title_command_editor_paint(rct_window * w, rct_drawpixelinfo w->widgets[WIDX_INPUT_DROPDOWN].left - w->widgets[WIDX_INPUT].left - 4); } } + else if (command.Type == TITLE_SCRIPT_LOADSC) + { + if (command.Scenario[0] == '\0') + { + gfx_draw_string_left_clipped( + dpi, + STR_TITLE_COMMAND_EDITOR_NO_SCENARIO_SELECTED, + nullptr, + w->colours[1], + w->x + w->widgets[WIDX_INPUT].left + 1, + w->y + w->widgets[WIDX_INPUT].top, + w->widgets[WIDX_INPUT_DROPDOWN].left - w->widgets[WIDX_INPUT].left - 4); + } + else + { + const char * name = ""; + rct_string_id nameString = STR_STRING; + auto scenario = + GetScenarioRepository()->GetByInternalName(command.Scenario); + if (scenario != nullptr) + { + name = scenario->name; + } + else + { + nameString = STR_TITLE_COMMAND_EDITOR_MISSING_SCENARIO; + } + set_format_arg(0, uintptr_t, name); + gfx_draw_string_left_clipped( + dpi, + nameString, + gCommonFormatArgs, + w->colours[1], + w->x + w->widgets[WIDX_INPUT].left + 1, + w->y + w->widgets[WIDX_INPUT].top, + w->widgets[WIDX_INPUT_DROPDOWN].left - w->widgets[WIDX_INPUT].left - 4); + } + } } diff --git a/src/openrct2-ui/windows/TitleEditor.cpp b/src/openrct2-ui/windows/TitleEditor.cpp index a11b77aabf..40867d3cb0 100644 --- a/src/openrct2-ui/windows/TitleEditor.cpp +++ b/src/openrct2-ui/windows/TitleEditor.cpp @@ -1002,6 +1002,27 @@ static void window_title_editor_scrollpaint_commands(rct_window * w, rct_drawpix set_format_arg(0, uintptr_t, name); break; } + case TITLE_SCRIPT_LOADSC: + { + commandName = STR_TITLE_EDITOR_COMMAND_LOAD_FILE; + const char * name = ""; + auto scenario = + GetScenarioRepository()->GetByInternalName(command->Scenario); + if (command->Scenario[0] == '\0') + { + commandName = STR_TITLE_EDITOR_COMMAND_LOAD_NO_SCENARIO; + } + else if (scenario != nullptr) + { + name = scenario->name; + } + else + { + commandName = STR_TITLE_EDITOR_COMMAND_LOAD_MISSING_SCENARIO; + } + set_format_arg(0, uintptr_t, name); + break; + } default: log_warning("Unknown command %d", command->Type); } diff --git a/src/openrct2-ui/windows/TitleMenu.cpp b/src/openrct2-ui/windows/TitleMenu.cpp index 0cb9037559..38ac190272 100644 --- a/src/openrct2-ui/windows/TitleMenu.cpp +++ b/src/openrct2-ui/windows/TitleMenu.cpp @@ -143,7 +143,7 @@ static void window_title_menu_mouseup(rct_window *w, rct_widgetindex widgetIndex else { window_close_by_class(WC_LOADSAVE); window_close_by_class(WC_SERVER_LIST); - window_scenarioselect_open(window_title_menu_scenarioselect_callback); + window_scenarioselect_open(window_title_menu_scenarioselect_callback, false); } break; case WIDX_CONTINUE_SAVED_GAME: diff --git a/src/openrct2-ui/windows/TitleScenarioSelect.cpp b/src/openrct2-ui/windows/TitleScenarioSelect.cpp index 2f2aaa8985..ce88d45de4 100644 --- a/src/openrct2-ui/windows/TitleScenarioSelect.cpp +++ b/src/openrct2-ui/windows/TitleScenarioSelect.cpp @@ -145,12 +145,13 @@ static bool is_locking_enabled(rct_window *w); static scenarioselect_callback _callback; static bool _showLockedInformation = false; +static bool _titleEditor = false; /** * * rct2: 0x006781B5 */ -rct_window * window_scenarioselect_open(scenarioselect_callback callback) +rct_window * window_scenarioselect_open(scenarioselect_callback callback, bool titleEditor) { rct_window* window; sint32 windowWidth; @@ -158,6 +159,12 @@ rct_window * window_scenarioselect_open(scenarioselect_callback callback) _callback = callback; + if (_titleEditor != titleEditor) + { + _titleEditor = titleEditor; + window_close_by_class(WC_SCENARIO_SELECT); + } + window = window_bring_to_front_by_class(WC_SCENARIO_SELECT); if (window != nullptr) return window; @@ -166,18 +173,17 @@ rct_window * window_scenarioselect_open(scenarioselect_callback callback) scenario_repository_scan(); // Shrink the window if we're showing scenarios by difficulty level. - if (gConfigGeneral.scenario_select_mode == SCENARIO_SELECT_MODE_DIFFICULTY) { + if (gConfigGeneral.scenario_select_mode == SCENARIO_SELECT_MODE_DIFFICULTY && !_titleEditor) windowWidth = 610; - } else { + else windowWidth = 733; - } window = window_create_centred( windowWidth, windowHeight, &window_scenarioselect_events, WC_SCENARIO_SELECT, - WF_10 + WF_10 | (titleEditor ? WF_STICK_TO_FRONT : 0) ); window->widgets = window_scenarioselect_widgets; window->enabled_widgets = (1 << WIDX_CLOSE) | (1 << WIDX_TAB1) | (1 << WIDX_TAB2) @@ -202,18 +208,25 @@ static void window_scenarioselect_init_tabs(rct_window *w) { sint32 showPages = 0; size_t numScenarios = scenario_repository_get_count(); - for (size_t i = 0; i < numScenarios; i++) { + for (size_t i = 0; i < numScenarios; i++) + { const scenario_index_entry *scenario = scenario_repository_get_by_index(i); - if (gConfigGeneral.scenario_select_mode == SCENARIO_SELECT_MODE_ORIGIN) { + if (gConfigGeneral.scenario_select_mode == SCENARIO_SELECT_MODE_ORIGIN || _titleEditor) + { + if (_titleEditor && scenario->source_game == SCENARIO_SOURCE_OTHER) + continue; showPages |= 1 << scenario->source_game; - } else { + } + else + { sint32 category = scenario->category; - if (category > SCENARIO_CATEGORY_OTHER) { + if (category > SCENARIO_CATEGORY_OTHER) + { category = SCENARIO_CATEGORY_OTHER; } showPages |= 1 << category; } - } + } sint32 firstPage = bitscanforward(showPages); if (firstPage != -1) { @@ -294,6 +307,10 @@ static void window_scenarioselect_scrollmousedown(rct_window *w, sint32 scrollIn audio_play_sound(SOUND_CLICK_1, 0, w->x + (w->width / 2)); gFirstTimeSaving = true; _callback(listItem->scenario.scenario->path); + if (_titleEditor) + { + window_close(w); + } } break; } @@ -383,7 +400,7 @@ static void window_scenarioselect_paint(rct_window *w, rct_drawpixelinfo *dpi) sint32 x = (widget->left + widget->right) / 2 + w->x; sint32 y = (widget->top + widget->bottom) / 2 + w->y - 3; - if (gConfigGeneral.scenario_select_mode == SCENARIO_SELECT_MODE_ORIGIN) { + if (gConfigGeneral.scenario_select_mode == SCENARIO_SELECT_MODE_ORIGIN || _titleEditor) { set_format_arg(0, rct_string_id, ScenarioOriginStringIds[i]); } else { // old-style set_format_arg(0, rct_string_id, ScenarioCategoryStringIds[i]); @@ -460,7 +477,7 @@ static void window_scenarioselect_scrollpaint(rct_window *w, rct_drawpixelinfo * rct_string_id highlighted_format = (theme_get_flags() & UITHEME_FLAG_USE_ALTERNATIVE_SCENARIO_SELECT_FONT) ? STR_WHITE_STRING : STR_WINDOW_COLOUR_2_STRINGID; rct_string_id unhighlighted_format = (theme_get_flags() & UITHEME_FLAG_USE_ALTERNATIVE_SCENARIO_SELECT_FONT) ? STR_WHITE_STRING : STR_BLACK_STRING; - bool wide = gConfigGeneral.scenario_select_mode == SCENARIO_SELECT_MODE_ORIGIN; + bool wide = gConfigGeneral.scenario_select_mode == SCENARIO_SELECT_MODE_ORIGIN || _titleEditor; rct_widget *listWidget = &w->widgets[WIDX_SCENARIOLIST]; sint32 listWidth = listWidget->right - listWidget->left - 12; @@ -573,15 +590,17 @@ static void initialise_list_items(rct_window *w) uint8 currentHeading = UINT8_MAX; for (size_t i = 0; i < numScenarios; i++) { const scenario_index_entry *scenario = scenario_repository_get_by_index(i); - if (!is_scenario_visible(w, scenario)) { + + if (!is_scenario_visible(w, scenario)) + continue; + if (_titleEditor && scenario->source_game == SCENARIO_SOURCE_OTHER) continue; - } sc_list_item *listItem; // Category heading rct_string_id headingStringId = STR_NONE; - if (gConfigGeneral.scenario_select_mode == SCENARIO_SELECT_MODE_ORIGIN) { + if (gConfigGeneral.scenario_select_mode == SCENARIO_SELECT_MODE_ORIGIN || _titleEditor) { if (w->selected_tab != SCENARIO_SOURCE_REAL && currentHeading != scenario->category) { currentHeading = scenario->category; headingStringId = ScenarioCategoryStringIds[currentHeading]; @@ -676,7 +695,7 @@ static void initialise_list_items(rct_window *w) static bool is_scenario_visible(rct_window *w, const scenario_index_entry *scenario) { - if (gConfigGeneral.scenario_select_mode == SCENARIO_SELECT_MODE_ORIGIN) { + if (gConfigGeneral.scenario_select_mode == SCENARIO_SELECT_MODE_ORIGIN || _titleEditor) { if (scenario->source_game != w->selected_tab) { return false; } @@ -694,14 +713,14 @@ static bool is_scenario_visible(rct_window *w, const scenario_index_entry *scena static bool is_locking_enabled(rct_window *w) { - if (gConfigGeneral.scenario_select_mode != SCENARIO_SELECT_MODE_ORIGIN) { + if (gConfigGeneral.scenario_select_mode != SCENARIO_SELECT_MODE_ORIGIN) return false; - } - if (!gConfigGeneral.scenario_unlocking_enabled) { + if (!gConfigGeneral.scenario_unlocking_enabled) return false; - } - if (w->selected_tab >= 6) { + if (w->selected_tab >= 6) return false; - } + if (_titleEditor) + return false; + return true; } diff --git a/src/openrct2-ui/windows/Window.h b/src/openrct2-ui/windows/Window.h index 121dd4d5ec..6a673b2608 100644 --- a/src/openrct2-ui/windows/Window.h +++ b/src/openrct2-ui/windows/Window.h @@ -87,7 +87,7 @@ rct_window * window_guest_list_open_with_filter(sint32 type, sint32 index); rct_window * window_staff_fire_prompt_open(rct_peep* peep); void window_title_editor_open(sint32 tab); void window_title_command_editor_open(struct TitleSequence * sequence, sint32 command, bool insert); -rct_window * window_scenarioselect_open(scenarioselect_callback callback); +rct_window * window_scenarioselect_open(scenarioselect_callback callback, bool titleEditor); rct_window * window_error_open(rct_string_id title, rct_string_id message); diff --git a/src/openrct2/localisation/string_ids.h b/src/openrct2/localisation/string_ids.h index b0dde5bf82..522e4167ac 100644 --- a/src/openrct2/localisation/string_ids.h +++ b/src/openrct2/localisation/string_ids.h @@ -3741,7 +3741,7 @@ enum { STR_CLASSIC_MINI_COASTER_GROUP_DESC = 6119, STR_NEWS_ITEM_RESEARCH_NEW_VEHICLE_AVAILABLE = 6120, - + STR_CHEAT_OWN_ALL_LAND_TIP = 6121, STR_NOT_ENOUGH_ROLLER_COASTERS = 6122, @@ -3798,7 +3798,7 @@ enum { STR_CONSOLE = 6157, STR_FAILED_TO_LOAD_IMCOMPATIBLE_RCTC_FLAG = 6158, - + STR_SCALING_QUALITY_SMOOTH_NN = 6159, STR_AVAILABLE_VEHICLES = 6160, @@ -3812,7 +3812,7 @@ enum { STR_USE_VSYNC = 6165, STR_USE_VSYNC_TIP = 6166, - + STR_OPTIONS_ADVANCED = 6167, STR_OPTIONS_TITLE_SEQUENCE = 6168, STR_OPTIONS_SCENARIO_SELECTION = 6169, @@ -3822,6 +3822,15 @@ enum { STR_GUESTS_FILTER_BY_NAME_TIP = 6172, STR_GUESTS_ENTER_NAME_TO_SEARCH = 6173, + STR_TITLE_EDITOR_ACTION_LOAD_SAVE = 6174, + STR_TITLE_EDITOR_ACTION_LOAD_SCENARIO = 6175, + STR_TITLE_EDITOR_ARGUMENT_SCENARIO = 6176, + STR_TITLE_EDITOR_COMMAND_LOAD_NO_SCENARIO = 6177, + STR_TITLE_EDITOR_COMMAND_LOAD_MISSING_SCENARIO = 6178, + STR_TITLE_COMMAND_EDITOR_ACTION_SELECT_SCENARIO = 6179, + STR_TITLE_COMMAND_EDITOR_NO_SCENARIO_SELECTED = 6180, + STR_TITLE_COMMAND_EDITOR_MISSING_SCENARIO = 6181, + // Have to include resource strings (from scenarios and objects) for the time being now that language is partially working STR_COUNT = 32768 }; diff --git a/src/openrct2/rct1/S4Importer.cpp b/src/openrct2/rct1/S4Importer.cpp index 796d6dfb2c..7984c190c1 100644 --- a/src/openrct2/rct1/S4Importer.cpp +++ b/src/openrct2/rct1/S4Importer.cpp @@ -269,6 +269,8 @@ public: desc.title = name.c_str(); } + String::Set(dst->internal_name, sizeof(dst->internal_name), desc.title); + rct_string_id localisedStringIds[3]; if (language_get_localised_scenario_strings(desc.title, localisedStringIds)) { diff --git a/src/openrct2/scenario/ScenarioRepository.cpp b/src/openrct2/scenario/ScenarioRepository.cpp index 1d0bbae822..fd4f9b5e90 100644 --- a/src/openrct2/scenario/ScenarioRepository.cpp +++ b/src/openrct2/scenario/ScenarioRepository.cpp @@ -123,7 +123,7 @@ class ScenarioFileIndex final : public FileIndex { private: static constexpr uint32 MAGIC_NUMBER = 0x58444953; // SIDX - static constexpr uint16 VERSION = 2; + static constexpr uint16 VERSION = 3; static constexpr auto PATTERN = "*.sc4;*.sc6"; public: @@ -170,6 +170,7 @@ protected: stream->WriteValue(item.objective_arg_2); stream->WriteValue(item.objective_arg_3); + stream->Write(item.internal_name, sizeof(item.internal_name)); stream->Write(item.name, sizeof(item.name)); stream->Write(item.details, sizeof(item.details)); } @@ -192,6 +193,7 @@ protected: item.objective_arg_3 = stream->ReadValue(); item.highscore = nullptr; + stream->Read(item.internal_name, sizeof(item.internal_name)); stream->Read(item.name, sizeof(item.name)); stream->Read(item.details, sizeof(item.details)); @@ -279,6 +281,9 @@ private: ScenarioSources::NormaliseName(entry.name, sizeof(entry.name), entry.name); } + // entry.name will be translated later so keep the untranslated name here + String::Set(entry.internal_name, sizeof(entry.internal_name), entry.name); + String::Set(entry.details, sizeof(entry.details), s6Info->details); // Look up and store information regarding the origins of this scenario. @@ -380,6 +385,21 @@ public: return nullptr; } + const scenario_index_entry * GetByInternalName(const utf8 * name) const override { + for (size_t i = 0; i < _scenarios.size(); i++) { + const scenario_index_entry * scenario = &_scenarios[i]; + + if (scenario->source_game == SCENARIO_SOURCE_OTHER) + continue; + + // Note: this is always case insensitive search for cross platform consistency + if (String::Equals(name, scenario->internal_name, true)) { + return &_scenarios[i]; + } + } + return nullptr; + } + const scenario_index_entry * GetByPath(const utf8 * path) const override { for (const auto &scenario : _scenarios) diff --git a/src/openrct2/scenario/ScenarioRepository.h b/src/openrct2/scenario/ScenarioRepository.h index 6f63874825..eb844a62b4 100644 --- a/src/openrct2/scenario/ScenarioRepository.h +++ b/src/openrct2/scenario/ScenarioRepository.h @@ -45,8 +45,9 @@ typedef struct scenario_index_entry sint32 objective_arg_2; sint16 objective_arg_3; scenario_highscore_entry * highscore; - - utf8 name[64]; + + utf8 internal_name[64]; // Untranslated name + utf8 name[64]; // Translated name utf8 details[256]; } scenario_index_entry; @@ -69,6 +70,10 @@ interface IScenarioRepository virtual size_t GetCount() const abstract; virtual const scenario_index_entry * GetByIndex(size_t index) const abstract; virtual const scenario_index_entry * GetByFilename(const utf8 * filename) const abstract; + /** + * Does not return custom scenarios due to the fact that they may have the same name. + */ + virtual const scenario_index_entry * GetByInternalName(const utf8 * name) const abstract; virtual const scenario_index_entry * GetByPath(const utf8 * path) const abstract; virtual bool TryRecordHighscore(const utf8 * scenarioFileName, money32 companyValue, const utf8 * name) abstract; diff --git a/src/openrct2/scenario/ScenarioSources.cpp b/src/openrct2/scenario/ScenarioSources.cpp index d7e90250de..0ed6b1a770 100644 --- a/src/openrct2/scenario/ScenarioSources.cpp +++ b/src/openrct2/scenario/ScenarioSources.cpp @@ -314,7 +314,7 @@ namespace ScenarioSources } } - outDesc->title = nullptr; + outDesc->title = ""; outDesc->id = SC_UNIDENTIFIED; outDesc->source = SCENARIO_SOURCE_OTHER; outDesc->index = -1; diff --git a/src/openrct2/title/TitleSequence.cpp b/src/openrct2/title/TitleSequence.cpp index 8c318a32c9..8ffa6b80d9 100644 --- a/src/openrct2/title/TitleSequence.cpp +++ b/src/openrct2/title/TitleSequence.cpp @@ -29,6 +29,9 @@ #include "../core/String.hpp" #include "../core/StringBuilder.hpp" #include "../core/Zip.h" +#include "../scenario/ScenarioRepository.h" +#include "../scenario/ScenarioSources.h" +#include "../util/Util.h" #include "TitleSequence.h" @@ -475,6 +478,16 @@ static std::vector LegacyScriptRead(utf8 * script, size_t scriptLe command.Type = TITLE_SCRIPT_LOADRCT1; command.SaveIndex = atoi(part1) & 0xFF; } + else if (_stricmp(token, "LOADSC") == 0) + { + command.Type = TITLE_SCRIPT_LOADSC; + // Confirm the scenario exists + //source_desc desc; + //if (ScenarioSources::TryGetByName(part1, &desc)) + //{ + safe_strcpy(command.Scenario, part1, sizeof(command.Scenario)); + //} + } } if (command.Type != TITLE_SCRIPT_UNDEFINED) { @@ -517,7 +530,8 @@ static void LegacyScriptGetLine(IStream * stream, char * parts) { if (!whitespace) { - if (part == 0 && cindex == 4 && _strnicmp(parts, "LOAD", 4) == 0) + if (part == 0 && ((cindex == 4 && _strnicmp(parts, "LOAD", 4) == 0) || + (cindex == 6 && _strnicmp(parts, "LOADSC", 6) == 0))) { load = true; } @@ -595,6 +609,17 @@ static utf8 * LegacyScriptWrite(TitleSequence * seq) sb.Append(seq->Saves[command->SaveIndex]); } break; + case TITLE_SCRIPT_LOADSC: + if (command->Scenario[0] == '\0') + { + sb.Append("LOADSC "); + } + else + { + sb.Append("LOADSC "); + sb.Append(command->Scenario); + } + break; case TITLE_SCRIPT_LOCATION: String::Format(buffer, sizeof(buffer), "LOCATION %u %u", command->X, command->Y); sb.Append(buffer); @@ -634,6 +659,7 @@ bool TitleSequenceIsLoadCommand(const TitleCommand * command) case TITLE_SCRIPT_LOADMM: case TITLE_SCRIPT_LOAD: case TITLE_SCRIPT_LOADRCT1: + case TITLE_SCRIPT_LOADSC: return true; default: return false; diff --git a/src/openrct2/title/TitleSequence.h b/src/openrct2/title/TitleSequence.h index be81010c9f..bf3015eb9b 100644 --- a/src/openrct2/title/TitleSequence.h +++ b/src/openrct2/title/TitleSequence.h @@ -18,6 +18,8 @@ #include "../common.h" +#define TITLE_COMMAND_SCENARIO_LENGTH 64 + typedef struct TitleCommand { uint8 Type; @@ -32,6 +34,7 @@ typedef struct TitleCommand uint8 Zoom; // ZOOM uint8 Speed; // SPEED uint16 Milliseconds; // WAIT + utf8 Scenario[TITLE_COMMAND_SCENARIO_LENGTH]; // LOADSC }; } TitleCommand; @@ -70,6 +73,7 @@ enum TITLE_SCRIPT TITLE_SCRIPT_LOOP, TITLE_SCRIPT_ENDLOOP, TITLE_SCRIPT_LOADRCT1, + TITLE_SCRIPT_LOADSC, }; #ifdef __cplusplus diff --git a/src/openrct2/title/TitleSequencePlayer.cpp b/src/openrct2/title/TitleSequencePlayer.cpp index b147c51c26..81dc930681 100644 --- a/src/openrct2/title/TitleSequencePlayer.cpp +++ b/src/openrct2/title/TitleSequencePlayer.cpp @@ -339,6 +339,21 @@ private: } break; } + case TITLE_SCRIPT_LOADSC: + { + bool loadSuccess = false; + auto scenario = GetScenarioRepository()->GetByInternalName(command->Scenario); + if (scenario != nullptr) + { + loadSuccess = LoadParkFromFile(scenario->path); + } + if (!loadSuccess) + { + Console::Error::WriteLine("Failed to load: \"%s\" for the title sequence.", command->Scenario); + return false; + } + break; + } } return true; }