diff --git a/src/openrct2-ui/windows/EditorObjectSelection.cpp b/src/openrct2-ui/windows/EditorObjectSelection.cpp index e0ca0b1c06..ceb0520fcf 100644 --- a/src/openrct2-ui/windows/EditorObjectSelection.cpp +++ b/src/openrct2-ui/windows/EditorObjectSelection.cpp @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include @@ -33,13 +32,11 @@ #include #include #include -#include #include #include #include #include -#include -#include +#include enum { FILTER_RCT2 = (1 << 0), @@ -235,9 +232,6 @@ const sint32 window_editor_object_selection_animation_divisor[] = { 4, 8, 2, 4, static void window_editor_object_set_page(rct_window *w, sint32 page); static void window_editor_object_selection_set_pressed_tab(rct_window *w); -static void window_editor_object_selection_select_default_objects(); -static void window_editor_object_selection_select_required_objects(); -static sint32 window_editor_object_selection_select_object(uint8 bh, sint32 flags, const rct_object_entry *entry); static sint32 get_object_from_object_selection(uint8 object_type, sint32 y); static void window_editor_object_selection_manage_tracks(); static void editor_load_selected_objects(); @@ -247,10 +241,6 @@ static bool filter_source(const ObjectRepositoryItem * item); static bool filter_chunks(const ObjectRepositoryItem * item); static void filter_update_counts(); -static void reset_selected_object_count_and_size(); -static sint32 sub_6AB211(); - - enum { RIDE_SORT_TYPE, RIDE_SORT_RIDE @@ -282,10 +272,6 @@ static list_item *_listItems = nullptr; static sint32 _listSortType = RIDE_SORT_TYPE; static bool _listSortDescending = false; static void * _loadedObject = nullptr; -static uint8 * _objectSelectionFlags = nullptr; -static sint32 _numSelectedObjectsForType[OBJECT_TYPE_COUNT]; -static sint32 _numAvailableObjectsForType[OBJECT_TYPE_COUNT]; -static bool _maxObjectsWasHit; static void visible_list_dispose() { @@ -447,268 +433,6 @@ rct_window * window_editor_object_selection_open() return window; } -/** - * - * rct2: 0x006ABCD1 - */ -static void setup_track_manager_objects() -{ - sint32 numObjects = (sint32)object_repository_get_items_count(); - const ObjectRepositoryItem * items = object_repository_get_items(); - for (sint32 i = 0; i < numObjects; i++) { - uint8 * selectionFlags = &_objectSelectionFlags[i]; - const ObjectRepositoryItem * item = &items[i]; - uint8 object_type = item->ObjectEntry.flags & 0xF; - if (object_type == OBJECT_TYPE_RIDE) { - *selectionFlags |= OBJECT_SELECTION_FLAG_6; - - for (uint8 j = 0; j < MAX_RIDE_TYPES_PER_RIDE_ENTRY; j++) { - uint8 rideType = item->RideType[j]; - if (rideType != RIDE_TYPE_NULL && ride_type_has_flag(rideType, RIDE_TYPE_FLAG_HAS_TRACK)) { - *selectionFlags &= ~OBJECT_SELECTION_FLAG_6; - break; - } - } - } - } -} - -/** - * - * rct2: 0x006ABC1E - */ -static void setup_track_designer_objects() -{ - sint32 numObjects = (sint32)object_repository_get_items_count(); - const ObjectRepositoryItem * items = object_repository_get_items(); - for (sint32 i = 0; i < numObjects; i++) { - uint8 * selectionFlags = &_objectSelectionFlags[i]; - const ObjectRepositoryItem * item = &items[i]; - uint8 objectType = item->ObjectEntry.flags & 0xF; - if (objectType == OBJECT_TYPE_RIDE){ - *selectionFlags |= OBJECT_SELECTION_FLAG_6; - - for (uint8 j = 0; j < MAX_RIDE_TYPES_PER_RIDE_ENTRY; j++) { - uint8 rideType = item->RideType[j]; - if (rideType != RIDE_TYPE_NULL) { - if (RideData4[rideType].flags & RIDE_TYPE_FLAG4_SHOW_IN_TRACK_DESIGNER) { - *selectionFlags &= ~OBJECT_SELECTION_FLAG_6; - break; - } - } - } - } - } -} - -/** - * - * rct2: 0x006AA82B - */ -static void setup_in_use_selection_flags() -{ - for (uint8 object_type = 0; object_type < OBJECT_TYPE_COUNT; object_type++){ - for (uint16 i = 0; i < object_entry_group_counts[object_type]; i++){ - Editor::SelectedObjects[object_type][i] = OBJECT_SELECTION_NOT_SELECTED_OR_REQUIRED; - } - } - - for (uint8 object_type = 0; object_type < OBJECT_TYPE_COUNT; object_type++){ - for (uint16 i = 0; i < object_entry_group_counts[object_type]; i++){ - if (object_entry_groups[object_type].chunks[i] != nullptr) { - Editor::SelectedObjects[object_type][i] |= OBJECT_SELECTION_FLAG_2; - } - } - } - - tile_element_iterator iter; - tile_element_iterator_begin(&iter); - do { - uint16 type; - rct_banner* banner; - - switch (tile_element_get_type(iter.element)) { - default: - case TILE_ELEMENT_TYPE_SURFACE: - case TILE_ELEMENT_TYPE_TRACK: - break; - case TILE_ELEMENT_TYPE_PATH: - type = iter.element->properties.path.type; - type >>= 4; - assert(type < object_entry_group_counts[OBJECT_TYPE_PATHS]); - Editor::SelectedObjects[OBJECT_TYPE_PATHS][type] |= OBJECT_SELECTION_FLAG_SELECTED; - - if (footpath_element_has_path_scenery(iter.element)) { - uint8 path_additions = footpath_element_get_path_scenery_index(iter.element); - Editor::SelectedObjects[OBJECT_TYPE_PATH_BITS][path_additions] |= OBJECT_SELECTION_FLAG_SELECTED; - } - break; - case TILE_ELEMENT_TYPE_SMALL_SCENERY: - type = iter.element->properties.scenery.type; - assert(type < object_entry_group_counts[OBJECT_TYPE_SMALL_SCENERY]); - Editor::SelectedObjects[OBJECT_TYPE_SMALL_SCENERY][type] |= OBJECT_SELECTION_FLAG_SELECTED; - break; - case TILE_ELEMENT_TYPE_ENTRANCE: - if (iter.element->properties.entrance.type != ENTRANCE_TYPE_PARK_ENTRANCE) - break; - - Editor::SelectedObjects[OBJECT_TYPE_PARK_ENTRANCE][0] |= OBJECT_SELECTION_FLAG_SELECTED; - - type = iter.element->properties.entrance.path_type; - assert(type < object_entry_group_counts[OBJECT_TYPE_PATHS]); - Editor::SelectedObjects[OBJECT_TYPE_PATHS][type] |= OBJECT_SELECTION_FLAG_SELECTED; - break; - case TILE_ELEMENT_TYPE_WALL: - type = iter.element->properties.wall.type; - assert(type < object_entry_group_counts[OBJECT_TYPE_WALLS]); - Editor::SelectedObjects[OBJECT_TYPE_WALLS][type] |= OBJECT_SELECTION_FLAG_SELECTED; - break; - case TILE_ELEMENT_TYPE_LARGE_SCENERY: - type = scenery_large_get_type(iter.element); - assert(type < object_entry_group_counts[OBJECT_TYPE_LARGE_SCENERY]); - Editor::SelectedObjects[OBJECT_TYPE_LARGE_SCENERY][type] |= OBJECT_SELECTION_FLAG_SELECTED; - break; - case TILE_ELEMENT_TYPE_BANNER: - banner = &gBanners[iter.element->properties.banner.index]; - type = banner->type; - assert(type < object_entry_group_counts[OBJECT_TYPE_BANNERS]); - Editor::SelectedObjects[OBJECT_TYPE_BANNERS][type] |= OBJECT_SELECTION_FLAG_SELECTED; - break; - } - } while (tile_element_iterator_next(&iter)); - - for (uint8 ride_index = 0; ride_index < 0xFF; ride_index++) { - Ride* ride = get_ride(ride_index); - if (ride->type != RIDE_TYPE_NULL) { - uint8 type = ride->subtype; - Editor::SelectedObjects[OBJECT_TYPE_RIDE][type] |= OBJECT_SELECTION_FLAG_SELECTED; - } - } - - sint32 numObjects = (sint32)object_repository_get_items_count(); - const ObjectRepositoryItem * items = object_repository_get_items(); - for (sint32 i = 0; i < numObjects; i++) { - uint8 *selectionFlags = &_objectSelectionFlags[i]; - const ObjectRepositoryItem * item = &items[i]; - *selectionFlags &= ~OBJECT_SELECTION_FLAG_IN_USE; - - uint8 entryType, entryIndex; - if (find_object_in_entry_group(&item->ObjectEntry, &entryType, &entryIndex)) { - if (Editor::SelectedObjects[entryType][entryIndex] & OBJECT_SELECTION_FLAG_SELECTED) { - *selectionFlags |= - OBJECT_SELECTION_FLAG_IN_USE | - OBJECT_SELECTION_FLAG_SELECTED; - } - if (Editor::SelectedObjects[entryType][entryIndex] & OBJECT_SELECTION_FLAG_2) { - *selectionFlags |= OBJECT_SELECTION_FLAG_SELECTED; - } - } - } -} - -/** - * - * rct2: 0x006AB211 - */ -static sint32 sub_6AB211() -{ - sint32 numObjects = (sint32)object_repository_get_items_count(); - _objectSelectionFlags = (uint8*)calloc(numObjects, sizeof(uint8)); - if (_objectSelectionFlags == nullptr){ - log_error("Failed to allocate memory for object flag list."); - return 0; - } - - for (uint8 objectType = 0; objectType < 11; objectType++) { - _numSelectedObjectsForType[objectType] = 0; - _numAvailableObjectsForType[objectType] = 0; - } - - const ObjectRepositoryItem * items = object_repository_get_items(); - for (sint32 i = 0; i < numObjects; i++) { - uint8 objectType = items[i].ObjectEntry.flags & 0xF; - _numAvailableObjectsForType[objectType]++; - } - - if (gScreenFlags & SCREEN_FLAGS_TRACK_DESIGNER) { - setup_track_designer_objects(); - } - - if (gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER) { - setup_track_manager_objects(); - } - - setup_in_use_selection_flags(); - reset_selected_object_count_and_size(); - - if (!(gScreenFlags & (SCREEN_FLAGS_TRACK_DESIGNER | SCREEN_FLAGS_TRACK_MANAGER))) { - window_editor_object_selection_select_required_objects(); - - // To prevent it breaking in scenario mode. - if (gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) { - window_editor_object_selection_select_default_objects(); - } - } - - reset_selected_object_count_and_size(); - return 1; -} - -/** - * - * rct2: 0x006AB316 - */ -static void editor_object_flags_free() -{ - SafeFree(_objectSelectionFlags); -} - -/** - * - * rct2: 0x00685791 - */ -static void remove_selected_objects_from_research(const rct_object_entry* installedObject){ - uint8 entry_type, entry_index; - if (!find_object_in_entry_group(installedObject, &entry_type, &entry_index)) - return; - - if (entry_type == OBJECT_TYPE_RIDE){ - rct_ride_entry* rideEntry = (rct_ride_entry*)object_entry_groups[entry_type].chunks[entry_index]; - - for (uint8 j = 0; j < MAX_RIDE_TYPES_PER_RIDE_ENTRY; j++) { - research_remove(entry_index | rideEntry->ride_type[j] << 8 | 0x10000); - } - } - else if (entry_type == OBJECT_TYPE_SCENERY_GROUP){ - research_remove(entry_index); - } -} - -/** - * - * rct2: 0x006ABB66 - */ -static void unload_unselected_objects() -{ - sint32 numItems = (sint32)object_repository_get_items_count(); - const ObjectRepositoryItem * items = object_repository_get_items(); - - size_t numObjectsToUnload = 0; - rct_object_entry * objectsToUnload = (rct_object_entry *)malloc(numItems * sizeof(rct_object_entry)); - - for (sint32 i = 0; i < numItems; i++) { - if (!(_objectSelectionFlags[i] & OBJECT_SELECTION_FLAG_SELECTED)) { - const rct_object_entry * entry = &items[i].ObjectEntry; - - remove_selected_objects_from_research(entry); - objectsToUnload[numObjectsToUnload++] = *entry; - } - } - - object_manager_unload_objects(objectsToUnload, numObjectsToUnload); - free(objectsToUnload); -} - /** * * rct2: 0x006AB199 @@ -1482,158 +1206,6 @@ static void window_editor_object_selection_set_pressed_tab(rct_window *w) w->pressed_widgets |= 1LL << (WIDX_TAB_1 + w->selected_tab); } -/** -* -* rct2: 0x006AA805 -*/ -static void window_editor_object_selection_select_default_objects() -{ - if (_numSelectedObjectsForType[0] == 0) { - for (sint32 i = 0; i < (sint32)Util::CountOf(DefaultSelectedObjects); i++) { - window_editor_object_selection_select_object(0, 7, &DefaultSelectedObjects[i]); - } - } -} - -/** - * - * rct2: 0x006AA7E9 - */ -static void window_editor_object_selection_select_required_objects() -{ - sint32 i; - - for (i = 0; i < (sint32)Util::CountOf(RequiredSelectedObjects); i++) - window_editor_object_selection_select_object(0, 0xF, &RequiredSelectedObjects[i]); -} - -/** - * - * rct2: 0x006AA770 - */ -static void reset_selected_object_count_and_size() -{ - for (uint8 objectType = 0; objectType < 11; objectType++) { - _numSelectedObjectsForType[objectType] = 0; - } - - sint32 numObjects = (sint32)object_repository_get_items_count(); - const ObjectRepositoryItem * items = object_repository_get_items(); - for (sint32 i = 0; i < numObjects; i++) { - uint8 objectType = items[i].ObjectEntry.flags & 0xF; - if (_objectSelectionFlags[i] & OBJECT_SELECTION_FLAG_SELECTED) { - _numSelectedObjectsForType[objectType]++; - } - } -} - -/** - * Master objects are objects that are not - * optional / required dependants of an - * object. - */ -static void set_object_selection_error(uint8 is_master_object, rct_string_id error_msg){ - gGameCommandErrorText = error_msg; - if (!is_master_object){ - reset_selected_object_count_and_size(); - } -} - -/** - * - * rct2: 0x006AB54F - */ -static sint32 window_editor_object_selection_select_object(uint8 bh, sint32 flags, const rct_object_entry *entry) -{ - sint32 numObjects = (sint32)object_repository_get_items_count(); - const ObjectRepositoryItem * item = object_repository_find_object_by_entry(entry); - if (item == nullptr) { - set_object_selection_error(bh, STR_OBJECT_SELECTION_ERR_OBJECT_DATA_NOT_FOUND); - return 0; - } - - // Get repository item index - sint32 index = -1; - const ObjectRepositoryItem * items = object_repository_get_items(); - for (sint32 i = 0; i < numObjects; i++) { - if (&items[i] == item) { - index = i; - } - } - - uint8 * selectionFlags = &_objectSelectionFlags[index]; - if (!(flags & 1)) { - if (!(*selectionFlags & OBJECT_SELECTION_FLAG_SELECTED)) { - return 1; - } - else if (*selectionFlags & OBJECT_SELECTION_FLAG_IN_USE) { - set_object_selection_error(bh, STR_OBJECT_SELECTION_ERR_CURRENTLY_IN_USE); - return 0; - } - else if (*selectionFlags & OBJECT_SELECTION_FLAG_ALWAYS_REQUIRED) { - set_object_selection_error(bh, STR_OBJECT_SELECTION_ERR_ALWAYS_REQUIRED); - return 0; - } - - uint8 objectType = item->ObjectEntry.flags & 0xF; - if (objectType == OBJECT_TYPE_SCENERY_GROUP && (flags & (1 << 2))) { - for (sint32 j = 0; j < item->NumThemeObjects; j++) { - window_editor_object_selection_select_object(++bh, flags, &item->ThemeObjects[j]); - } - } - - _numSelectedObjectsForType[objectType]--; - *selectionFlags &= ~OBJECT_SELECTION_FLAG_SELECTED; - return 1; - } else { - if (bh == 0) { - if (flags & (1 << 3)) { - *selectionFlags |= OBJECT_SELECTION_FLAG_ALWAYS_REQUIRED; - } - } - if (*selectionFlags & OBJECT_SELECTION_FLAG_SELECTED) { - return 1; - } - - uint8 objectType = item->ObjectEntry.flags & 0xF; - uint16 maxObjects = object_entry_group_counts[objectType]; - if (gScreenFlags & SCREEN_FLAGS_TRACK_DESIGNER) { - maxObjects = 4; - } - - if (maxObjects <= _numSelectedObjectsForType[objectType]) { - set_object_selection_error(bh, STR_OBJECT_SELECTION_ERR_TOO_MANY_OF_TYPE_SELECTED); - return 0; - } - - if (objectType == OBJECT_TYPE_SCENERY_GROUP && (flags & (1 << 2))) { - for (uint16 j = 0; j < item->NumThemeObjects; j++) { - if (!window_editor_object_selection_select_object(++bh, flags, &item->ThemeObjects[j])) { - _maxObjectsWasHit = true; - } - } - } - - if (bh != 0 && !(flags & (1 << 1))) { - char objectName[64]; - object_create_identifier_name(objectName, 64, &item->ObjectEntry); - set_format_arg(0, const char *, objectName); - set_object_selection_error(bh, STR_OBJECT_SELECTION_ERR_SHOULD_SELECT_X_FIRST); - return 0; - } - - if (maxObjects <= _numSelectedObjectsForType[objectType]) { - set_object_selection_error(bh, STR_OBJECT_SELECTION_ERR_TOO_MANY_OF_TYPE_SELECTED); - return 0; - } - - _numSelectedObjectsForType[objectType]++; - - *selectionFlags |= OBJECT_SELECTION_FLAG_SELECTED; - return 1; - } -} - /** * Takes the y coordinate of the clicked on scroll list * and converts this into an object selection. @@ -1881,61 +1453,3 @@ static rct_string_id get_ride_type_string_id(const ObjectRepositoryItem * item) } return result; } - -bool editor_check_object_group_at_least_one_selected(sint32 checkObjectType) -{ - sint32 numObjects = (sint32)object_repository_get_items_count(); - const ObjectRepositoryItem * items = object_repository_get_items(); - - for (sint32 i = 0; i < numObjects; i++) { - uint8 objectType = items[i].ObjectEntry.flags & 0x0F; - if (checkObjectType == objectType && (_objectSelectionFlags[i] & OBJECT_SELECTION_FLAG_SELECTED)) { - return true; - } - } - return false; -} - -sint32 editor_remove_unused_objects() -{ - bool createSelectionFlags = (_objectSelectionFlags == nullptr); - if (createSelectionFlags && !sub_6AB211()) - { - return 0; - } - - setup_in_use_selection_flags(); - - sint32 numObjects = (sint32)object_repository_get_items_count(); - const ObjectRepositoryItem * items = object_repository_get_items(); - - sint32 numUnselectedObjects = 0; - for (sint32 i = 0; i < numObjects; i++) - { - if (!(_objectSelectionFlags[i] & OBJECT_SELECTION_FLAG_IN_USE) && !(_objectSelectionFlags[i] & OBJECT_SELECTION_FLAG_ALWAYS_REQUIRED)) - { - const ObjectRepositoryItem * item = &items[i]; - uint8 objectType = item->ObjectEntry.flags & 0xF; - - if (objectType == OBJECT_TYPE_PARK_ENTRANCE || objectType == OBJECT_TYPE_SCENARIO_TEXT || objectType == OBJECT_TYPE_WATER || objectType == OBJECT_TYPE_SCENERY_GROUP) - { - continue; - } - - _numSelectedObjectsForType[objectType]--; - _objectSelectionFlags[i] &= ~OBJECT_SELECTION_FLAG_SELECTED; - numUnselectedObjects++; - } - } - unload_unselected_objects(); - - if (createSelectionFlags) - { - editor_object_flags_free(); - } - - auto intent = Intent(INTENT_ACTION_REFRESH_SCENERY); - context_broadcast_intent(&intent); - - return numUnselectedObjects; -} diff --git a/src/openrct2/Editor.cpp b/src/openrct2/Editor.cpp index 2a1b6c84d7..13e9247dc2 100644 --- a/src/openrct2/Editor.cpp +++ b/src/openrct2/Editor.cpp @@ -33,6 +33,7 @@ #include "windows/Intent.h" #include "world/Climate.h" #include "object/ObjectRepository.h" +#include "EditorObjectSelectionSession.h" namespace Editor { diff --git a/src/openrct2/Editor.h b/src/openrct2/Editor.h index 1d5bff2ee5..84c2930f0c 100644 --- a/src/openrct2/Editor.h +++ b/src/openrct2/Editor.h @@ -40,8 +40,6 @@ namespace Editor void GameCommandEditScenarioOptions(sint32*, sint32*, sint32*, sint32*, sint32*, sint32*, sint32*); } -bool editor_check_object_group_at_least_one_selected(sint32 objectType); - extern "C" { #endif @@ -84,12 +82,6 @@ extern "C" }; void editor_open_windows_for_current_step(); - - /** - * Removes all unused objects from the object selection. - * @return The number of removed objects. - */ - sint32 editor_remove_unused_objects(); void game_command_edit_scenario_options(sint32* eax, sint32* ebx, sint32* ecx, sint32* edx, sint32* esi, sint32* edi, sint32* ebp); #ifdef __cplusplus diff --git a/src/openrct2/EditorObjectSelectionSession.cpp b/src/openrct2/EditorObjectSelectionSession.cpp new file mode 100644 index 0000000000..a8db64b948 --- /dev/null +++ b/src/openrct2/EditorObjectSelectionSession.cpp @@ -0,0 +1,513 @@ +#pragma region Copyright (c) 2014-2017 OpenRCT2 Developers +/***************************************************************************** + * OpenRCT2, an open source clone of Roller Coaster Tycoon 2. + * + * OpenRCT2 is the work of many authors, a full list can be found in contributors.md + * For more information, visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * A full copy of the GNU General Public License can be found in licence.txt + *****************************************************************************/ +#pragma endregion + +#include "Context.h" +#include "core/Util.hpp" +#include "Editor.h" +#include "EditorObjectSelectionSession.h" +#include "Game.h" +#include "localisation/localisation.h" +#include "object/ObjectManager.h" +#include "object/ObjectRepository.h" +#include "OpenRCT2.h" +#include "ride/ride_data.h" +#include "windows/Intent.h" +#include "windows/_legacy.h" +#include "world/footpath.h" +#include "world/LargeScenery.h" + +bool _maxObjectsWasHit; +uint8 * _objectSelectionFlags = nullptr; +sint32 _numSelectedObjectsForType[OBJECT_TYPE_COUNT]; +static sint32 _numAvailableObjectsForType[OBJECT_TYPE_COUNT]; + +static void setup_in_use_selection_flags(); +static void setup_track_designer_objects(); +static void setup_track_manager_objects(); +static void window_editor_object_selection_select_required_objects(); +static void window_editor_object_selection_select_default_objects(); + +/** + * + * rct2: 0x006ABCD1 + */ +static void setup_track_manager_objects() +{ + sint32 numObjects = (sint32)object_repository_get_items_count(); + const ObjectRepositoryItem * items = object_repository_get_items(); + for (sint32 i = 0; i < numObjects; i++) { + uint8 * selectionFlags = &_objectSelectionFlags[i]; + const ObjectRepositoryItem * item = &items[i]; + uint8 object_type = item->ObjectEntry.flags & 0xF; + if (object_type == OBJECT_TYPE_RIDE) { + *selectionFlags |= OBJECT_SELECTION_FLAG_6; + + for (uint8 j = 0; j < MAX_RIDE_TYPES_PER_RIDE_ENTRY; j++) { + uint8 rideType = item->RideType[j]; + if (rideType != RIDE_TYPE_NULL && ride_type_has_flag(rideType, RIDE_TYPE_FLAG_HAS_TRACK)) { + *selectionFlags &= ~OBJECT_SELECTION_FLAG_6; + break; + } + } + } + } +} + +/** + * + * rct2: 0x006ABC1E + */ +static void setup_track_designer_objects() +{ + sint32 numObjects = (sint32)object_repository_get_items_count(); + const ObjectRepositoryItem * items = object_repository_get_items(); + for (sint32 i = 0; i < numObjects; i++) { + uint8 * selectionFlags = &_objectSelectionFlags[i]; + const ObjectRepositoryItem * item = &items[i]; + uint8 objectType = item->ObjectEntry.flags & 0xF; + if (objectType == OBJECT_TYPE_RIDE){ + *selectionFlags |= OBJECT_SELECTION_FLAG_6; + + for (uint8 j = 0; j < MAX_RIDE_TYPES_PER_RIDE_ENTRY; j++) { + uint8 rideType = item->RideType[j]; + if (rideType != RIDE_TYPE_NULL) { + if (RideData4[rideType].flags & RIDE_TYPE_FLAG4_SHOW_IN_TRACK_DESIGNER) { + *selectionFlags &= ~OBJECT_SELECTION_FLAG_6; + break; + } + } + } + } + } +} + +/** + * + * rct2: 0x006AA82B + */ +void setup_in_use_selection_flags() +{ + for (uint8 object_type = 0; object_type < OBJECT_TYPE_COUNT; object_type++){ + for (uint16 i = 0; i < object_entry_group_counts[object_type]; i++){ + Editor::SelectedObjects[object_type][i] = OBJECT_SELECTION_NOT_SELECTED_OR_REQUIRED; + } + } + + for (uint8 object_type = 0; object_type < OBJECT_TYPE_COUNT; object_type++){ + for (uint16 i = 0; i < object_entry_group_counts[object_type]; i++){ + if (object_entry_groups[object_type].chunks[i] != nullptr) { + Editor::SelectedObjects[object_type][i] |= OBJECT_SELECTION_FLAG_2; + } + } + } + + tile_element_iterator iter; + tile_element_iterator_begin(&iter); + do { + uint16 type; + rct_banner* banner; + + switch (tile_element_get_type(iter.element)) { + default: + case TILE_ELEMENT_TYPE_SURFACE: + case TILE_ELEMENT_TYPE_TRACK: + break; + case TILE_ELEMENT_TYPE_PATH: + type = iter.element->properties.path.type; + type >>= 4; + assert(type < object_entry_group_counts[OBJECT_TYPE_PATHS]); + Editor::SelectedObjects[OBJECT_TYPE_PATHS][type] |= OBJECT_SELECTION_FLAG_SELECTED; + + if (footpath_element_has_path_scenery(iter.element)) { + uint8 path_additions = footpath_element_get_path_scenery_index(iter.element); + Editor::SelectedObjects[OBJECT_TYPE_PATH_BITS][path_additions] |= OBJECT_SELECTION_FLAG_SELECTED; + } + break; + case TILE_ELEMENT_TYPE_SMALL_SCENERY: + type = iter.element->properties.scenery.type; + assert(type < object_entry_group_counts[OBJECT_TYPE_SMALL_SCENERY]); + Editor::SelectedObjects[OBJECT_TYPE_SMALL_SCENERY][type] |= OBJECT_SELECTION_FLAG_SELECTED; + break; + case TILE_ELEMENT_TYPE_ENTRANCE: + if (iter.element->properties.entrance.type != ENTRANCE_TYPE_PARK_ENTRANCE) + break; + + Editor::SelectedObjects[OBJECT_TYPE_PARK_ENTRANCE][0] |= OBJECT_SELECTION_FLAG_SELECTED; + + type = iter.element->properties.entrance.path_type; + assert(type < object_entry_group_counts[OBJECT_TYPE_PATHS]); + Editor::SelectedObjects[OBJECT_TYPE_PATHS][type] |= OBJECT_SELECTION_FLAG_SELECTED; + break; + case TILE_ELEMENT_TYPE_WALL: + type = iter.element->properties.wall.type; + assert(type < object_entry_group_counts[OBJECT_TYPE_WALLS]); + Editor::SelectedObjects[OBJECT_TYPE_WALLS][type] |= OBJECT_SELECTION_FLAG_SELECTED; + break; + case TILE_ELEMENT_TYPE_LARGE_SCENERY: + type = scenery_large_get_type(iter.element); + assert(type < object_entry_group_counts[OBJECT_TYPE_LARGE_SCENERY]); + Editor::SelectedObjects[OBJECT_TYPE_LARGE_SCENERY][type] |= OBJECT_SELECTION_FLAG_SELECTED; + break; + case TILE_ELEMENT_TYPE_BANNER: + banner = &gBanners[iter.element->properties.banner.index]; + type = banner->type; + assert(type < object_entry_group_counts[OBJECT_TYPE_BANNERS]); + Editor::SelectedObjects[OBJECT_TYPE_BANNERS][type] |= OBJECT_SELECTION_FLAG_SELECTED; + break; + } + } while (tile_element_iterator_next(&iter)); + + for (uint8 ride_index = 0; ride_index < 0xFF; ride_index++) { + Ride* ride = get_ride(ride_index); + if (ride->type != RIDE_TYPE_NULL) { + uint8 type = ride->subtype; + Editor::SelectedObjects[OBJECT_TYPE_RIDE][type] |= OBJECT_SELECTION_FLAG_SELECTED; + } + } + + sint32 numObjects = (sint32)object_repository_get_items_count(); + const ObjectRepositoryItem * items = object_repository_get_items(); + for (sint32 i = 0; i < numObjects; i++) { + uint8 *selectionFlags = &_objectSelectionFlags[i]; + const ObjectRepositoryItem * item = &items[i]; + *selectionFlags &= ~OBJECT_SELECTION_FLAG_IN_USE; + + uint8 entryType, entryIndex; + if (find_object_in_entry_group(&item->ObjectEntry, &entryType, &entryIndex)) { + if (Editor::SelectedObjects[entryType][entryIndex] & OBJECT_SELECTION_FLAG_SELECTED) { + *selectionFlags |= + OBJECT_SELECTION_FLAG_IN_USE | + OBJECT_SELECTION_FLAG_SELECTED; + } + if (Editor::SelectedObjects[entryType][entryIndex] & OBJECT_SELECTION_FLAG_2) { + *selectionFlags |= OBJECT_SELECTION_FLAG_SELECTED; + } + } + } +} + +/** + * + * rct2: 0x006AB211 + */ +bool sub_6AB211() +{ + sint32 numObjects = (sint32)object_repository_get_items_count(); + _objectSelectionFlags = (uint8*)calloc(numObjects, sizeof(uint8)); + if (_objectSelectionFlags == nullptr){ + log_error("Failed to allocate memory for object flag list."); + return false; + } + + for (uint8 objectType = 0; objectType < 11; objectType++) { + _numSelectedObjectsForType[objectType] = 0; + _numAvailableObjectsForType[objectType] = 0; + } + + const ObjectRepositoryItem * items = object_repository_get_items(); + for (sint32 i = 0; i < numObjects; i++) { + uint8 objectType = items[i].ObjectEntry.flags & 0xF; + _numAvailableObjectsForType[objectType]++; + } + + if (gScreenFlags & SCREEN_FLAGS_TRACK_DESIGNER) { + setup_track_designer_objects(); + } + + if (gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER) { + setup_track_manager_objects(); + } + + setup_in_use_selection_flags(); + reset_selected_object_count_and_size(); + + if (!(gScreenFlags & (SCREEN_FLAGS_TRACK_DESIGNER | SCREEN_FLAGS_TRACK_MANAGER))) { + window_editor_object_selection_select_required_objects(); + + // To prevent it breaking in scenario mode. + if (gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) { + window_editor_object_selection_select_default_objects(); + } + } + + reset_selected_object_count_and_size(); + return true; +} + +/** + * + * rct2: 0x006AB316 + */ +void editor_object_flags_free() +{ + SafeFree(_objectSelectionFlags); +} + +/** + * + * rct2: 0x00685791 + */ +static void remove_selected_objects_from_research(const rct_object_entry* installedObject){ + uint8 entry_type, entry_index; + if (!find_object_in_entry_group(installedObject, &entry_type, &entry_index)) + return; + + if (entry_type == OBJECT_TYPE_RIDE){ + rct_ride_entry* rideEntry = (rct_ride_entry*)object_entry_groups[entry_type].chunks[entry_index]; + + for (uint8 j = 0; j < MAX_RIDE_TYPES_PER_RIDE_ENTRY; j++) { + research_remove(entry_index | rideEntry->ride_type[j] << 8 | 0x10000); + } + } + else if (entry_type == OBJECT_TYPE_SCENERY_GROUP){ + research_remove(entry_index); + } +} + +/** + * + * rct2: 0x006ABB66 + */ +void unload_unselected_objects() +{ + sint32 numItems = (sint32)object_repository_get_items_count(); + const ObjectRepositoryItem * items = object_repository_get_items(); + + size_t numObjectsToUnload = 0; + rct_object_entry * objectsToUnload = (rct_object_entry *)malloc(numItems * sizeof(rct_object_entry)); + + for (sint32 i = 0; i < numItems; i++) { + if (!(_objectSelectionFlags[i] & OBJECT_SELECTION_FLAG_SELECTED)) { + const rct_object_entry * entry = &items[i].ObjectEntry; + + remove_selected_objects_from_research(entry); + objectsToUnload[numObjectsToUnload++] = *entry; + } + } + + object_manager_unload_objects(objectsToUnload, numObjectsToUnload); + free(objectsToUnload); +} + +/** +* +* rct2: 0x006AA805 +*/ +static void window_editor_object_selection_select_default_objects() +{ + if (_numSelectedObjectsForType[0] == 0) { + for (sint32 i = 0; i < (sint32)Util::CountOf(DefaultSelectedObjects); i++) { + window_editor_object_selection_select_object(0, 7, &DefaultSelectedObjects[i]); + } + } +} + +/** + * + * rct2: 0x006AA7E9 + */ +static void window_editor_object_selection_select_required_objects() +{ + sint32 i; + + for (i = 0; i < (sint32)Util::CountOf(RequiredSelectedObjects); i++) + window_editor_object_selection_select_object(0, 0xF, &RequiredSelectedObjects[i]); +} + +/** + * + * rct2: 0x006AA770 + */ +void reset_selected_object_count_and_size() +{ + for (uint8 objectType = 0; objectType < 11; objectType++) { + _numSelectedObjectsForType[objectType] = 0; + } + + sint32 numObjects = (sint32)object_repository_get_items_count(); + const ObjectRepositoryItem * items = object_repository_get_items(); + for (sint32 i = 0; i < numObjects; i++) { + uint8 objectType = items[i].ObjectEntry.flags & 0xF; + if (_objectSelectionFlags[i] & OBJECT_SELECTION_FLAG_SELECTED) { + _numSelectedObjectsForType[objectType]++; + } + } +} + +/** + * Master objects are objects that are not + * optional / required dependants of an + * object. + */ +static void set_object_selection_error(uint8 is_master_object, rct_string_id error_msg){ + gGameCommandErrorText = error_msg; + if (!is_master_object){ + reset_selected_object_count_and_size(); + } +} + +/** + * + * rct2: 0x006AB54F + */ +sint32 window_editor_object_selection_select_object(uint8 bh, sint32 flags, const rct_object_entry *entry) +{ + sint32 numObjects = (sint32)object_repository_get_items_count(); + const ObjectRepositoryItem * item = object_repository_find_object_by_entry(entry); + if (item == nullptr) { + set_object_selection_error(bh, STR_OBJECT_SELECTION_ERR_OBJECT_DATA_NOT_FOUND); + return 0; + } + + // Get repository item index + sint32 index = -1; + const ObjectRepositoryItem * items = object_repository_get_items(); + for (sint32 i = 0; i < numObjects; i++) { + if (&items[i] == item) { + index = i; + } + } + + uint8 * selectionFlags = &_objectSelectionFlags[index]; + if (!(flags & 1)) { + if (!(*selectionFlags & OBJECT_SELECTION_FLAG_SELECTED)) { + return 1; + } + else if (*selectionFlags & OBJECT_SELECTION_FLAG_IN_USE) { + set_object_selection_error(bh, STR_OBJECT_SELECTION_ERR_CURRENTLY_IN_USE); + return 0; + } + else if (*selectionFlags & OBJECT_SELECTION_FLAG_ALWAYS_REQUIRED) { + set_object_selection_error(bh, STR_OBJECT_SELECTION_ERR_ALWAYS_REQUIRED); + return 0; + } + + uint8 objectType = item->ObjectEntry.flags & 0xF; + if (objectType == OBJECT_TYPE_SCENERY_GROUP && (flags & (1 << 2))) { + for (sint32 j = 0; j < item->NumThemeObjects; j++) { + window_editor_object_selection_select_object(++bh, flags, &item->ThemeObjects[j]); + } + } + + _numSelectedObjectsForType[objectType]--; + *selectionFlags &= ~OBJECT_SELECTION_FLAG_SELECTED; + return 1; + } else { + if (bh == 0) { + if (flags & (1 << 3)) { + *selectionFlags |= OBJECT_SELECTION_FLAG_ALWAYS_REQUIRED; + } + } + if (*selectionFlags & OBJECT_SELECTION_FLAG_SELECTED) { + return 1; + } + + uint8 objectType = item->ObjectEntry.flags & 0xF; + uint16 maxObjects = object_entry_group_counts[objectType]; + if (gScreenFlags & SCREEN_FLAGS_TRACK_DESIGNER) { + maxObjects = 4; + } + + if (maxObjects <= _numSelectedObjectsForType[objectType]) { + set_object_selection_error(bh, STR_OBJECT_SELECTION_ERR_TOO_MANY_OF_TYPE_SELECTED); + return 0; + } + + if (objectType == OBJECT_TYPE_SCENERY_GROUP && (flags & (1 << 2))) { + for (uint16 j = 0; j < item->NumThemeObjects; j++) { + if (!window_editor_object_selection_select_object(++bh, flags, &item->ThemeObjects[j])) { + _maxObjectsWasHit = true; + } + } + } + + if (bh != 0 && !(flags & (1 << 1))) { + char objectName[64]; + object_create_identifier_name(objectName, 64, &item->ObjectEntry); + set_format_arg(0, const char *, objectName); + set_object_selection_error(bh, STR_OBJECT_SELECTION_ERR_SHOULD_SELECT_X_FIRST); + return 0; + } + + if (maxObjects <= _numSelectedObjectsForType[objectType]) { + set_object_selection_error(bh, STR_OBJECT_SELECTION_ERR_TOO_MANY_OF_TYPE_SELECTED); + return 0; + } + + _numSelectedObjectsForType[objectType]++; + + *selectionFlags |= OBJECT_SELECTION_FLAG_SELECTED; + return 1; + } +} + +bool editor_check_object_group_at_least_one_selected(sint32 checkObjectType) +{ + sint32 numObjects = (sint32)object_repository_get_items_count(); + const ObjectRepositoryItem * items = object_repository_get_items(); + + for (sint32 i = 0; i < numObjects; i++) { + uint8 objectType = items[i].ObjectEntry.flags & 0x0F; + if (checkObjectType == objectType && (_objectSelectionFlags[i] & OBJECT_SELECTION_FLAG_SELECTED)) { + return true; + } + } + return false; +} + +sint32 editor_remove_unused_objects() +{ + bool createSelectionFlags = (_objectSelectionFlags == nullptr); + if (createSelectionFlags && !sub_6AB211()) + { + return 0; + } + + setup_in_use_selection_flags(); + + sint32 numObjects = (sint32)object_repository_get_items_count(); + const ObjectRepositoryItem * items = object_repository_get_items(); + + sint32 numUnselectedObjects = 0; + for (sint32 i = 0; i < numObjects; i++) + { + if (!(_objectSelectionFlags[i] & OBJECT_SELECTION_FLAG_IN_USE) && !(_objectSelectionFlags[i] & OBJECT_SELECTION_FLAG_ALWAYS_REQUIRED)) + { + const ObjectRepositoryItem * item = &items[i]; + uint8 objectType = item->ObjectEntry.flags & 0xF; + + if (objectType == OBJECT_TYPE_PARK_ENTRANCE || objectType == OBJECT_TYPE_SCENARIO_TEXT || objectType == OBJECT_TYPE_WATER || objectType == OBJECT_TYPE_SCENERY_GROUP) + { + continue; + } + + _numSelectedObjectsForType[objectType]--; + _objectSelectionFlags[i] &= ~OBJECT_SELECTION_FLAG_SELECTED; + numUnselectedObjects++; + } + } + unload_unselected_objects(); + + if (createSelectionFlags) + { + editor_object_flags_free(); + } + + auto intent = Intent(INTENT_ACTION_REFRESH_SCENERY); + context_broadcast_intent(&intent); + + return numUnselectedObjects; +} diff --git a/src/openrct2/EditorObjectSelectionSession.h b/src/openrct2/EditorObjectSelectionSession.h new file mode 100644 index 0000000000..b1c6d2c095 --- /dev/null +++ b/src/openrct2/EditorObjectSelectionSession.h @@ -0,0 +1,48 @@ +#pragma region Copyright (c) 2014-2017 OpenRCT2 Developers +/***************************************************************************** + * OpenRCT2, an open source clone of Roller Coaster Tycoon 2. + * + * OpenRCT2 is the work of many authors, a full list can be found in contributors.md + * For more information, visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * A full copy of the GNU General Public License can be found in licence.txt + *****************************************************************************/ +#pragma endregion + +#pragma once + +#include "common.h" +#include "object.h" + +#ifdef __cplusplus +extern bool _maxObjectsWasHit; +extern uint8 * _objectSelectionFlags; +extern sint32 _numSelectedObjectsForType[OBJECT_TYPE_COUNT]; + +bool editor_check_object_group_at_least_one_selected(sint32 objectType); +void editor_object_flags_free(); +void unload_unselected_objects(); +bool sub_6AB211(); +void reset_selected_object_count_and_size(); +sint32 window_editor_object_selection_select_object(uint8 bh, sint32 flags, const rct_object_entry *entry); +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + + /** + * Removes all unused objects from the object selection. + * @return The number of removed objects. + */ + sint32 editor_remove_unused_objects(); + +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/src/openrct2/interface/console.c b/src/openrct2/interface/console.c index 3926d424d6..086c8690e7 100644 --- a/src/openrct2/interface/console.c +++ b/src/openrct2/interface/console.c @@ -20,6 +20,7 @@ #include "../Context.h" #include "../drawing/drawing.h" #include "../Editor.h" +#include "../EditorObjectSelectionSession.h" #include "../Game.h" #include "../input.h" #include "../interface/themes.h"