1 /*****************************************************************************
2  * Copyright (c) 2014-2020 OpenRCT2 developers
3  *
4  * For a complete list of all authors, please refer to contributors.md
5  * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
6  *
7  * OpenRCT2 is licensed under the GNU General Public License version 3.
8  *****************************************************************************/
9 
10 #include "EditorObjectSelectionSession.h"
11 
12 #include "Context.h"
13 #include "Editor.h"
14 #include "Game.h"
15 #include "OpenRCT2.h"
16 #include "drawing/Drawing.h"
17 #include "localisation/Localisation.h"
18 #include "management/Research.h"
19 #include "object/DefaultObjects.h"
20 #include "object/ObjectList.h"
21 #include "object/ObjectManager.h"
22 #include "object/ObjectRepository.h"
23 #include "ride/RideData.h"
24 #include "ride/TrainManager.h"
25 #include "ride/Vehicle.h"
26 #include "scenario/Scenario.h"
27 #include "windows/Intent.h"
28 #include "world/Footpath.h"
29 #include "world/LargeScenery.h"
30 
31 #include <iterator>
32 #include <vector>
33 
34 bool _maxObjectsWasHit;
35 std::vector<uint8_t> _objectSelectionFlags;
36 int32_t _numSelectedObjectsForType[EnumValue(ObjectType::Count)];
37 static int32_t _numAvailableObjectsForType[EnumValue(ObjectType::Count)];
38 
39 static void setup_in_use_selection_flags();
40 static void setup_track_designer_objects();
41 static void setup_track_manager_objects();
42 static void window_editor_object_selection_select_default_objects();
43 static void SelectDesignerObjects();
44 static void ReplaceSelectedWaterPalette(const ObjectRepositoryItem* item);
45 
46 /**
47  *
48  *  rct2: 0x006ABCD1
49  */
setup_track_manager_objects()50 static void setup_track_manager_objects()
51 {
52     int32_t numObjects = static_cast<int32_t>(object_repository_get_items_count());
53     const ObjectRepositoryItem* items = object_repository_get_items();
54     for (int32_t i = 0; i < numObjects; i++)
55     {
56         uint8_t* selectionFlags = &_objectSelectionFlags[i];
57         const ObjectRepositoryItem* item = &items[i];
58         if (item->Type == ObjectType::Ride)
59         {
60             *selectionFlags |= OBJECT_SELECTION_FLAG_6;
61 
62             for (auto rideType : item->RideInfo.RideType)
63             {
64                 if (GetRideTypeDescriptor(rideType).HasFlag(RIDE_TYPE_FLAG_HAS_TRACK))
65                 {
66                     *selectionFlags &= ~OBJECT_SELECTION_FLAG_6;
67                     break;
68                 }
69             }
70         }
71     }
72 }
73 
74 /**
75  *
76  *  rct2: 0x006ABC1E
77  */
setup_track_designer_objects()78 static void setup_track_designer_objects()
79 {
80     int32_t numObjects = static_cast<int32_t>(object_repository_get_items_count());
81     const ObjectRepositoryItem* items = object_repository_get_items();
82     SelectDesignerObjects();
83     for (int32_t i = 0; i < numObjects; i++)
84     {
85         uint8_t* selectionFlags = &_objectSelectionFlags[i];
86         const ObjectRepositoryItem* item = &items[i];
87         if (item->Type == ObjectType::Ride)
88         {
89             *selectionFlags |= OBJECT_SELECTION_FLAG_6;
90 
91             for (uint8_t rideType : item->RideInfo.RideType)
92             {
93                 if (rideType != RIDE_TYPE_NULL)
94                 {
95                     if (GetRideTypeDescriptor(rideType).HasFlag(RIDE_TYPE_FLAG_SHOW_IN_TRACK_DESIGNER))
96                     {
97                         *selectionFlags &= ~OBJECT_SELECTION_FLAG_6;
98                         break;
99                     }
100                 }
101             }
102         }
103     }
104 }
105 
106 /**
107  *
108  *  rct2: 0x006AA82B
109  */
setup_in_use_selection_flags()110 void setup_in_use_selection_flags()
111 {
112     auto& objectMgr = OpenRCT2::GetContext()->GetObjectManager();
113 
114     for (uint8_t objectType = 0; objectType < EnumValue(ObjectType::Count); objectType++)
115     {
116         for (int32_t i = 0; i < object_entry_group_counts[objectType]; i++)
117         {
118             Editor::ClearSelectedObject(static_cast<ObjectType>(objectType), i, OBJECT_SELECTION_FLAG_ALL);
119 
120             auto loadedObj = objectMgr.GetLoadedObject(static_cast<ObjectType>(objectType), i);
121             if (loadedObj != nullptr)
122             {
123                 Editor::SetSelectedObject(static_cast<ObjectType>(objectType), i, OBJECT_SELECTION_FLAG_2);
124             }
125         }
126     }
127 
128     tile_element_iterator iter;
129     tile_element_iterator_begin(&iter);
130     do
131     {
132         ObjectEntryIndex type;
133 
134         switch (iter.element->GetType())
135         {
136             default:
137             case TILE_ELEMENT_TYPE_SURFACE:
138             case TILE_ELEMENT_TYPE_TRACK:
139                 break;
140             case TILE_ELEMENT_TYPE_PATH:
141                 type = iter.element->AsPath()->GetLegacyPathEntryIndex();
142                 assert(type < object_entry_group_counts[EnumValue(ObjectType::Paths)]);
143                 Editor::SetSelectedObject(ObjectType::Paths, type, OBJECT_SELECTION_FLAG_SELECTED);
144 
145                 if (iter.element->AsPath()->HasAddition())
146                 {
147                     uint8_t path_additions = iter.element->AsPath()->GetAdditionEntryIndex();
148                     Editor::SetSelectedObject(ObjectType::PathBits, path_additions, OBJECT_SELECTION_FLAG_SELECTED);
149                 }
150                 break;
151             case TILE_ELEMENT_TYPE_SMALL_SCENERY:
152                 type = iter.element->AsSmallScenery()->GetEntryIndex();
153                 assert(type < object_entry_group_counts[EnumValue(ObjectType::SmallScenery)]);
154                 Editor::SetSelectedObject(ObjectType::SmallScenery, type, OBJECT_SELECTION_FLAG_SELECTED);
155                 break;
156             case TILE_ELEMENT_TYPE_ENTRANCE:
157                 if (iter.element->AsEntrance()->GetEntranceType() != ENTRANCE_TYPE_PARK_ENTRANCE)
158                     break;
159                 // Skip if not the middle part
160                 if (iter.element->AsEntrance()->GetSequenceIndex() != 0)
161                     break;
162 
163                 Editor::SetSelectedObject(ObjectType::ParkEntrance, 0, OBJECT_SELECTION_FLAG_SELECTED);
164 
165                 type = iter.element->AsEntrance()->GetLegacyPathEntryIndex();
166                 assert(type < object_entry_group_counts[EnumValue(ObjectType::Paths)]);
167                 Editor::SetSelectedObject(ObjectType::Paths, type, OBJECT_SELECTION_FLAG_SELECTED);
168                 break;
169             case TILE_ELEMENT_TYPE_WALL:
170                 type = iter.element->AsWall()->GetEntryIndex();
171                 assert(type < object_entry_group_counts[EnumValue(ObjectType::Walls)]);
172                 Editor::SetSelectedObject(ObjectType::Walls, type, OBJECT_SELECTION_FLAG_SELECTED);
173                 break;
174             case TILE_ELEMENT_TYPE_LARGE_SCENERY:
175                 type = iter.element->AsLargeScenery()->GetEntryIndex();
176                 assert(type < object_entry_group_counts[EnumValue(ObjectType::LargeScenery)]);
177                 Editor::SetSelectedObject(ObjectType::LargeScenery, type, OBJECT_SELECTION_FLAG_SELECTED);
178                 break;
179             case TILE_ELEMENT_TYPE_BANNER:
180             {
181                 auto banner = iter.element->AsBanner()->GetBanner();
182                 if (banner != nullptr)
183                 {
184                     type = banner->type;
185                     assert(type < object_entry_group_counts[EnumValue(ObjectType::Banners)]);
186                     Editor::SetSelectedObject(ObjectType::Banners, type, OBJECT_SELECTION_FLAG_SELECTED);
187                 }
188                 break;
189             }
190         }
191     } while (tile_element_iterator_next(&iter));
192 
193     for (auto& ride : GetRideManager())
194     {
195         ObjectEntryIndex type = ride.subtype;
196         Editor::SetSelectedObject(ObjectType::Ride, type, OBJECT_SELECTION_FLAG_SELECTED);
197     }
198 
199     // Apply selected object status for hacked vehicles that may not have an associated ride
200     for (auto* vehicle : TrainManager::View())
201     {
202         ObjectEntryIndex type = vehicle->ride_subtype;
203         if (type != OBJECT_ENTRY_INDEX_NULL) // cable lifts use index null. Ignore them
204         {
205             Editor::SetSelectedObject(ObjectType::Ride, type, OBJECT_SELECTION_FLAG_SELECTED);
206         }
207     }
208     for (auto vehicle : EntityList<Vehicle>())
209     {
210         ObjectEntryIndex type = vehicle->ride_subtype;
211         if (type != OBJECT_ENTRY_INDEX_NULL) // cable lifts use index null. Ignore them
212         {
213             Editor::SetSelectedObject(ObjectType::Ride, type, OBJECT_SELECTION_FLAG_SELECTED);
214         }
215     }
216 
217     int32_t numObjects = static_cast<int32_t>(object_repository_get_items_count());
218     const ObjectRepositoryItem* items = object_repository_get_items();
219     for (int32_t i = 0; i < numObjects; i++)
220     {
221         uint8_t* selectionFlags = &_objectSelectionFlags[i];
222         const ObjectRepositoryItem* item = &items[i];
223         *selectionFlags &= ~OBJECT_SELECTION_FLAG_IN_USE;
224 
225         ObjectType entryType;
226         ObjectEntryIndex entryIndex;
227         if (find_object_in_entry_group(&item->ObjectEntry, &entryType, &entryIndex))
228         {
229             auto flags = Editor::GetSelectedObjectFlags(entryType, entryIndex);
230             if (flags & OBJECT_SELECTION_FLAG_SELECTED)
231             {
232                 *selectionFlags |= OBJECT_SELECTION_FLAG_IN_USE | OBJECT_SELECTION_FLAG_SELECTED;
233             }
234             if (flags & OBJECT_SELECTION_FLAG_2)
235             {
236                 *selectionFlags |= OBJECT_SELECTION_FLAG_SELECTED;
237             }
238         }
239     }
240 }
241 
242 /**
243  *
244  *  rct2: 0x006AB211
245  */
sub_6AB211()246 void sub_6AB211()
247 {
248     int32_t numObjects = static_cast<int32_t>(object_repository_get_items_count());
249     _objectSelectionFlags = std::vector<uint8_t>(numObjects);
250 
251     for (uint8_t objectType = 0; objectType < EnumValue(ObjectType::Count); objectType++)
252     {
253         _numSelectedObjectsForType[objectType] = 0;
254         _numAvailableObjectsForType[objectType] = 0;
255     }
256 
257     const ObjectRepositoryItem* items = object_repository_get_items();
258     for (int32_t i = 0; i < numObjects; i++)
259     {
260         ObjectType objectType = items[i].Type;
261         _numAvailableObjectsForType[EnumValue(objectType)]++;
262     }
263 
264     if (gScreenFlags & SCREEN_FLAGS_TRACK_DESIGNER)
265     {
266         setup_track_designer_objects();
267     }
268 
269     if (gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER)
270     {
271         setup_track_manager_objects();
272     }
273 
274     setup_in_use_selection_flags();
275     reset_selected_object_count_and_size();
276 
277     if (!(gScreenFlags & (SCREEN_FLAGS_TRACK_DESIGNER | SCREEN_FLAGS_TRACK_MANAGER)))
278     {
279         // To prevent it breaking in scenario mode.
280         if (gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR)
281         {
282             window_editor_object_selection_select_default_objects();
283         }
284     }
285 
286     reset_selected_object_count_and_size();
287 }
288 
289 /**
290  *
291  *  rct2: 0x006AB316
292  */
editor_object_flags_free()293 void editor_object_flags_free()
294 {
295     _objectSelectionFlags.clear();
296     _objectSelectionFlags.shrink_to_fit();
297 }
298 
299 /**
300  *
301  *  rct2: 0x00685791
302  */
remove_selected_objects_from_research(ObjectEntryDescriptor & descriptor)303 static void remove_selected_objects_from_research(ObjectEntryDescriptor& descriptor)
304 {
305     auto& objManager = OpenRCT2::GetContext()->GetObjectManager();
306     auto obj = objManager.GetLoadedObject(descriptor);
307     if (obj != nullptr)
308     {
309         auto entryIndex = objManager.GetLoadedObjectEntryIndex(obj);
310         switch (obj->GetObjectType())
311         {
312             case ObjectType::Ride:
313             {
314                 auto rideEntry = get_ride_entry(entryIndex);
315                 for (auto rideType : rideEntry->ride_type)
316                 {
317                     ResearchItem tmp = {};
318                     tmp.type = Research::EntryType::Ride;
319                     tmp.entryIndex = entryIndex;
320                     tmp.baseRideType = rideType;
321                     ResearchRemove(tmp);
322                 }
323                 break;
324             }
325             case ObjectType::SceneryGroup:
326             {
327                 ResearchItem tmp = {};
328                 tmp.type = Research::EntryType::Scenery;
329                 tmp.entryIndex = entryIndex;
330                 ResearchRemove(tmp);
331                 break;
332             }
333             default:
334                 break;
335         }
336     }
337 }
338 
339 /**
340  *
341  *  rct2: 0x006ABB66
342  */
unload_unselected_objects()343 void unload_unselected_objects()
344 {
345     auto numItems = static_cast<int32_t>(object_repository_get_items_count());
346     const auto* items = object_repository_get_items();
347     std::vector<ObjectEntryDescriptor> objectsToUnload;
348 
349     for (int32_t i = 0; i < numItems; i++)
350     {
351         if (!(_objectSelectionFlags[i] & OBJECT_SELECTION_FLAG_SELECTED))
352         {
353             auto descriptor = ObjectEntryDescriptor(items[i]);
354             remove_selected_objects_from_research(descriptor);
355             objectsToUnload.push_back(descriptor);
356         }
357     }
358     object_manager_unload_objects(objectsToUnload);
359 }
360 
361 /**
362  *
363  *  rct2: 0x006AA805
364  */
window_editor_object_selection_select_default_objects()365 static void window_editor_object_selection_select_default_objects()
366 {
367     if (_numSelectedObjectsForType[0] == 0)
368     {
369         for (auto defaultSelectedObject : DefaultSelectedObjects)
370         {
371             window_editor_object_selection_select_object(
372                 0,
373                 INPUT_FLAG_EDITOR_OBJECT_SELECT | INPUT_FLAG_EDITOR_OBJECT_1
374                     | INPUT_FLAG_EDITOR_OBJECT_SELECT_OBJECTS_IN_SCENERY_GROUP,
375                 ObjectEntryDescriptor(defaultSelectedObject));
376         }
377     }
378 }
379 
SelectDesignerObjects()380 static void SelectDesignerObjects()
381 {
382     if (_numSelectedObjectsForType[0] == 0)
383     {
384         for (auto designerSelectedObject : DesignerSelectedObjects)
385         {
386             window_editor_object_selection_select_object(
387                 0,
388                 INPUT_FLAG_EDITOR_OBJECT_SELECT | INPUT_FLAG_EDITOR_OBJECT_1
389                     | INPUT_FLAG_EDITOR_OBJECT_SELECT_OBJECTS_IN_SCENERY_GROUP,
390                 ObjectEntryDescriptor(designerSelectedObject));
391         }
392     }
393 }
394 
395 /**
396  * Replaces the previously selected water palette with the palette in the specified item immediately.
397  */
ReplaceSelectedWaterPalette(const ObjectRepositoryItem * item)398 static void ReplaceSelectedWaterPalette(const ObjectRepositoryItem* item)
399 {
400     auto& objectManager = OpenRCT2::GetContext()->GetObjectManager();
401     auto* oldPalette = objectManager.GetLoadedObject(ObjectType::Water, 0);
402 
403     if (oldPalette != nullptr)
404     {
405         const std::vector<ObjectEntryDescriptor> oldEntries = { oldPalette->GetDescriptor() };
406         objectManager.UnloadObjects(oldEntries);
407     }
408 
409     auto newPaletteEntry = ObjectEntryDescriptor(*item);
410     if (objectManager.GetLoadedObject(newPaletteEntry) != nullptr || objectManager.LoadObject(newPaletteEntry) != nullptr)
411     {
412         load_palette();
413     }
414     else
415     {
416         log_error("Failed to load selected palette %s", std::string(newPaletteEntry.GetName()).c_str());
417     }
418 }
419 
420 /**
421  *
422  *  rct2: 0x006AA770
423  */
reset_selected_object_count_and_size()424 void reset_selected_object_count_and_size()
425 {
426     for (auto& objectType : _numSelectedObjectsForType)
427     {
428         objectType = 0;
429     }
430 
431     int32_t numObjects = static_cast<int32_t>(object_repository_get_items_count());
432     const ObjectRepositoryItem* items = object_repository_get_items();
433     for (int32_t i = 0; i < numObjects; i++)
434     {
435         ObjectType objectType = items[i].Type;
436         if (_objectSelectionFlags[i] & OBJECT_SELECTION_FLAG_SELECTED)
437         {
438             _numSelectedObjectsForType[EnumValue(objectType)]++;
439         }
440     }
441 }
442 
finish_object_selection()443 void finish_object_selection()
444 {
445     if (gScreenFlags & SCREEN_FLAGS_TRACK_DESIGNER)
446     {
447         set_every_ride_type_invented();
448         set_every_ride_entry_invented();
449         gEditorStep = EditorStep::RollercoasterDesigner;
450         gfx_invalidate_screen();
451     }
452     else
453     {
454         set_all_scenery_items_invented();
455         scenery_set_default_placement_configuration();
456         gEditorStep = EditorStep::LandscapeEditor;
457         gfx_invalidate_screen();
458     }
459 }
460 
461 /**
462  * Master objects are objects that are not
463  * optional / required dependants of an
464  * object.
465  */
set_object_selection_error(uint8_t is_master_object,rct_string_id error_msg)466 static void set_object_selection_error(uint8_t is_master_object, rct_string_id error_msg)
467 {
468     gGameCommandErrorText = error_msg;
469     if (!is_master_object)
470     {
471         reset_selected_object_count_and_size();
472     }
473 }
474 
475 /**
476  *
477  *  rct2: 0x006AB54F
478  */
window_editor_object_selection_select_object(uint8_t isMasterObject,int32_t flags,const ObjectRepositoryItem * item)479 bool window_editor_object_selection_select_object(uint8_t isMasterObject, int32_t flags, const ObjectRepositoryItem* item)
480 {
481     if (item == nullptr)
482     {
483         set_object_selection_error(isMasterObject, STR_OBJECT_SELECTION_ERR_OBJECT_DATA_NOT_FOUND);
484         return false;
485     }
486 
487     int32_t numObjects = static_cast<int32_t>(object_repository_get_items_count());
488     // Get repository item index
489     int32_t index = -1;
490     const ObjectRepositoryItem* items = object_repository_get_items();
491     for (int32_t i = 0; i < numObjects; i++)
492     {
493         if (&items[i] == item)
494         {
495             index = i;
496         }
497     }
498 
499     uint8_t* selectionFlags = &_objectSelectionFlags[index];
500     if (!(flags & INPUT_FLAG_EDITOR_OBJECT_SELECT))
501     {
502         if (!(*selectionFlags & OBJECT_SELECTION_FLAG_SELECTED))
503         {
504             return true;
505         }
506 
507         if (*selectionFlags & OBJECT_SELECTION_FLAG_IN_USE)
508         {
509             set_object_selection_error(isMasterObject, STR_OBJECT_SELECTION_ERR_CURRENTLY_IN_USE);
510             return false;
511         }
512 
513         if (*selectionFlags & OBJECT_SELECTION_FLAG_ALWAYS_REQUIRED)
514         {
515             set_object_selection_error(isMasterObject, STR_OBJECT_SELECTION_ERR_ALWAYS_REQUIRED);
516             return false;
517         }
518 
519         ObjectType objectType = item->Type;
520         if (objectType == ObjectType::SceneryGroup && (flags & INPUT_FLAG_EDITOR_OBJECT_SELECT_OBJECTS_IN_SCENERY_GROUP))
521         {
522             for (const auto& sgEntry : item->SceneryGroupInfo.Entries)
523             {
524                 window_editor_object_selection_select_object(++isMasterObject, flags, sgEntry);
525             }
526         }
527 
528         _numSelectedObjectsForType[EnumValue(objectType)]--;
529         *selectionFlags &= ~OBJECT_SELECTION_FLAG_SELECTED;
530         return true;
531     }
532 
533     if (isMasterObject == 0)
534     {
535         if (flags & INPUT_FLAG_EDITOR_OBJECT_ALWAYS_REQUIRED)
536         {
537             *selectionFlags |= OBJECT_SELECTION_FLAG_ALWAYS_REQUIRED;
538         }
539     }
540 
541     if (*selectionFlags & OBJECT_SELECTION_FLAG_SELECTED)
542     {
543         return true;
544     }
545 
546     ObjectType objectType = item->Type;
547     uint16_t maxObjects = object_entry_group_counts[EnumValue(objectType)];
548 
549     if (maxObjects <= _numSelectedObjectsForType[EnumValue(objectType)])
550     {
551         set_object_selection_error(isMasterObject, STR_OBJECT_SELECTION_ERR_TOO_MANY_OF_TYPE_SELECTED);
552         return false;
553     }
554 
555     if (objectType == ObjectType::SceneryGroup && (flags & INPUT_FLAG_EDITOR_OBJECT_SELECT_OBJECTS_IN_SCENERY_GROUP))
556     {
557         for (const auto& sgEntry : item->SceneryGroupInfo.Entries)
558         {
559             if (!window_editor_object_selection_select_object(++isMasterObject, flags, sgEntry))
560             {
561                 _maxObjectsWasHit = true;
562             }
563         }
564     }
565     else if (objectType == ObjectType::Water)
566     {
567         // Replace old palette with newly selected palette immediately.
568         ReplaceSelectedWaterPalette(item);
569     }
570 
571     if (isMasterObject != 0 && !(flags & INPUT_FLAG_EDITOR_OBJECT_1))
572     {
573         char objectName[64];
574         object_create_identifier_name(objectName, 64, &item->ObjectEntry);
575         auto ft = Formatter::Common();
576         ft.Add<const char*>(objectName);
577         set_object_selection_error(isMasterObject, STR_OBJECT_SELECTION_ERR_SHOULD_SELECT_X_FIRST);
578         return false;
579     }
580 
581     if (maxObjects <= _numSelectedObjectsForType[EnumValue(objectType)])
582     {
583         set_object_selection_error(isMasterObject, STR_OBJECT_SELECTION_ERR_TOO_MANY_OF_TYPE_SELECTED);
584         return false;
585     }
586 
587     _numSelectedObjectsForType[EnumValue(objectType)]++;
588 
589     *selectionFlags |= OBJECT_SELECTION_FLAG_SELECTED;
590     return true;
591 }
592 
window_editor_object_selection_select_object(uint8_t isMasterObject,int32_t flags,const ObjectEntryDescriptor & descriptor)593 bool window_editor_object_selection_select_object(
594     uint8_t isMasterObject, int32_t flags, const ObjectEntryDescriptor& descriptor)
595 {
596     auto& objectRepository = OpenRCT2::GetContext()->GetObjectRepository();
597     const auto* item = objectRepository.FindObject(descriptor);
598     return window_editor_object_selection_select_object(isMasterObject, flags, item);
599 }
600 
editor_check_object_group_at_least_one_selected(ObjectType checkObjectType)601 bool editor_check_object_group_at_least_one_selected(ObjectType checkObjectType)
602 {
603     auto numObjects = std::min(object_repository_get_items_count(), _objectSelectionFlags.size());
604     const ObjectRepositoryItem* items = object_repository_get_items();
605 
606     for (size_t i = 0; i < numObjects; i++)
607     {
608         auto objectType = items[i].Type;
609         if (checkObjectType == objectType && (_objectSelectionFlags[i] & OBJECT_SELECTION_FLAG_SELECTED))
610         {
611             return true;
612         }
613     }
614     return false;
615 }
616 
editor_check_object_group_at_least_one_surface_selected(bool queue)617 bool editor_check_object_group_at_least_one_surface_selected(bool queue)
618 {
619     auto numObjects = std::min(object_repository_get_items_count(), _objectSelectionFlags.size());
620     const auto* items = object_repository_get_items();
621     for (size_t i = 0; i < numObjects; i++)
622     {
623         const auto& ori = items[i];
624         auto isQueue = (ori.FootpathSurfaceInfo.Flags & FOOTPATH_ENTRY_FLAG_IS_QUEUE) != 0;
625         if (ori.Type == ObjectType::FootpathSurface && (_objectSelectionFlags[i] & OBJECT_SELECTION_FLAG_SELECTED)
626             && queue == isQueue)
627         {
628             return true;
629         }
630     }
631     return false;
632 }
633 
editor_remove_unused_objects()634 int32_t editor_remove_unused_objects()
635 {
636     sub_6AB211();
637     setup_in_use_selection_flags();
638 
639     int32_t numObjects = static_cast<int32_t>(object_repository_get_items_count());
640     const ObjectRepositoryItem* items = object_repository_get_items();
641 
642     int32_t numUnselectedObjects = 0;
643     for (int32_t i = 0; i < numObjects; i++)
644     {
645         if (_objectSelectionFlags[i] & OBJECT_SELECTION_FLAG_SELECTED)
646         {
647             if (!(_objectSelectionFlags[i] & OBJECT_SELECTION_FLAG_IN_USE)
648                 && !(_objectSelectionFlags[i] & OBJECT_SELECTION_FLAG_ALWAYS_REQUIRED))
649             {
650                 const ObjectRepositoryItem* item = &items[i];
651                 ObjectType objectType = item->Type;
652 
653                 if (objectType >= ObjectType::SceneryGroup)
654                 {
655                     continue;
656                 }
657 
658                 _numSelectedObjectsForType[EnumValue(objectType)]--;
659                 _objectSelectionFlags[i] &= ~OBJECT_SELECTION_FLAG_SELECTED;
660                 numUnselectedObjects++;
661             }
662         }
663     }
664     unload_unselected_objects();
665     editor_object_flags_free();
666 
667     auto intent = Intent(INTENT_ACTION_REFRESH_SCENERY);
668     context_broadcast_intent(&intent);
669 
670     return numUnselectedObjects;
671 }
672