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