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; }