From c8506ccd6f5ffe0941369c73b1652c9c86095297 Mon Sep 17 00:00:00 2001 From: Gymnasiast Date: Sat, 25 Oct 2025 13:43:15 +0200 Subject: [PATCH 1/3] Add method to allow object loading by ObjectRepositoryItem --- src/openrct2/object/ObjectManager.cpp | 5 +++++ src/openrct2/object/ObjectManager.h | 1 + 2 files changed, 6 insertions(+) diff --git a/src/openrct2/object/ObjectManager.cpp b/src/openrct2/object/ObjectManager.cpp index b0fe705aa2..714d1290cc 100644 --- a/src/openrct2/object/ObjectManager.cpp +++ b/src/openrct2/object/ObjectManager.cpp @@ -202,6 +202,11 @@ namespace OpenRCT2 return RepositoryItemToObject(ori, slot); } + Object* LoadRepositoryItem(const ObjectRepositoryItem& ori) override + { + return RepositoryItemToObject(&ori); + } + void LoadObjects(const ObjectList& objectList, const bool reportProgress) override { // Find all the required objects diff --git a/src/openrct2/object/ObjectManager.h b/src/openrct2/object/ObjectManager.h index 3e1420e0c2..ebd128a314 100644 --- a/src/openrct2/object/ObjectManager.h +++ b/src/openrct2/object/ObjectManager.h @@ -44,6 +44,7 @@ namespace OpenRCT2 virtual Object* LoadObject(const RCTObjectEntry* entry) = 0; virtual Object* LoadObject(const ObjectEntryDescriptor& descriptor) = 0; virtual Object* LoadObject(const ObjectEntryDescriptor& descriptor, ObjectEntryIndex slot) = 0; + virtual Object* LoadRepositoryItem(const ObjectRepositoryItem& ori) = 0; virtual void LoadObjects(const ObjectList& entries, const bool reportProgress = false) = 0; virtual void UnloadObjects(const std::vector& entries) = 0; virtual void UnloadAllTransient() = 0; From e3d9cc1c96cfa7231254f077444e54d2b1273ab8 Mon Sep 17 00:00:00 2001 From: Gymnasiast Date: Thu, 23 Oct 2025 17:22:36 +0200 Subject: [PATCH 2/3] Unwrap long if --- src/openrct2/interface/InteractiveConsole.cpp | 143 +++++++++--------- 1 file changed, 73 insertions(+), 70 deletions(-) diff --git a/src/openrct2/interface/InteractiveConsole.cpp b/src/openrct2/interface/InteractiveConsole.cpp index e24fe91d41..40ebc58f55 100644 --- a/src/openrct2/interface/InteractiveConsole.cpp +++ b/src/openrct2/interface/InteractiveConsole.cpp @@ -1051,78 +1051,81 @@ static void ConsoleCommandSet(InteractiveConsole& console, const arguments_t& ar static void ConsoleCommandLoadObject(InteractiveConsole& console, const arguments_t& argv) { - if (!argv.empty()) + if (argv.empty()) { - char name[9] = { 0 }; - std::fill_n(name, 8, ' '); - std::size_t i = 0; - for (const char* ch = argv[0].c_str(); *ch != '\0' && i < std::size(name) - 1; ch++) - { - name[i++] = *ch; - } - - const ObjectRepositoryItem* ori = ObjectRepositoryFindObjectByName(name); - if (ori == nullptr) - { - console.WriteLineError("Could not find the object."); - return; - } - - const auto* entry = &ori->ObjectEntry; - const auto* loadedObject = ObjectManagerGetLoadedObject(ObjectEntryDescriptor(*ori)); - if (loadedObject != nullptr) - { - console.WriteLineError("Object is already in scenario."); - return; - } - - loadedObject = ObjectManagerLoadObject(entry); - if (loadedObject == nullptr) - { - console.WriteLineError("Unable to load object."); - return; - } - auto groupIndex = ObjectManagerGetLoadedObjectEntryIndex(loadedObject); - - ObjectType objectType = entry->GetType(); - if (objectType == ObjectType::ride) - { - // Automatically research the ride so it's supported by the game. - const auto* rideEntry = GetRideEntryByIndex(groupIndex); - - for (int32_t j = 0; j < RCT2::ObjectLimits::kMaxRideTypesPerRideEntry; j++) - { - auto rideType = rideEntry->ride_type[j]; - if (rideType != kRideTypeNull) - { - ResearchCategory category = GetRideTypeDescriptor(rideType).GetResearchCategory(); - ResearchInsertRideEntry(rideType, groupIndex, category, true); - } - } - - gSilentResearch = true; - ResearchResetCurrentItem(); - gSilentResearch = false; - } - else if (objectType == ObjectType::sceneryGroup) - { - ResearchInsertSceneryGroupEntry(groupIndex, true); - - gSilentResearch = true; - ResearchResetCurrentItem(); - gSilentResearch = false; - } - - auto sceneryIntent = Intent(INTENT_ACTION_SET_DEFAULT_SCENERY_CONFIG); - ContextBroadcastIntent(&sceneryIntent); - - auto ridesIntent = Intent(INTENT_ACTION_REFRESH_NEW_RIDES); - ContextBroadcastIntent(&ridesIntent); - - gWindowUpdateTicks = 0; - GfxInvalidateScreen(); - console.WriteLine("Object file loaded."); + console.WriteLineError("Please specify an object name."); + return; } + + char name[9] = { 0 }; + std::fill_n(name, 8, ' '); + std::size_t i = 0; + for (const char* ch = argv[0].c_str(); *ch != '\0' && i < std::size(name) - 1; ch++) + { + name[i++] = *ch; + } + + const ObjectRepositoryItem* ori = ObjectRepositoryFindObjectByName(name); + if (ori == nullptr) + { + console.WriteLineError("Could not find the object."); + return; + } + + const auto* entry = &ori->ObjectEntry; + const auto* loadedObject = ObjectManagerGetLoadedObject(ObjectEntryDescriptor(*ori)); + if (loadedObject != nullptr) + { + console.WriteLineError("Object is already in scenario."); + return; + } + + loadedObject = ObjectManagerLoadObject(entry); + if (loadedObject == nullptr) + { + console.WriteLineError("Unable to load object."); + return; + } + auto groupIndex = ObjectManagerGetLoadedObjectEntryIndex(loadedObject); + + ObjectType objectType = entry->GetType(); + if (objectType == ObjectType::ride) + { + // Automatically research the ride so it's supported by the game. + const auto* rideEntry = GetRideEntryByIndex(groupIndex); + + for (int32_t j = 0; j < RCT2::ObjectLimits::kMaxRideTypesPerRideEntry; j++) + { + auto rideType = rideEntry->ride_type[j]; + if (rideType != kRideTypeNull) + { + ResearchCategory category = GetRideTypeDescriptor(rideType).GetResearchCategory(); + ResearchInsertRideEntry(rideType, groupIndex, category, true); + } + } + + gSilentResearch = true; + ResearchResetCurrentItem(); + gSilentResearch = false; + } + else if (objectType == ObjectType::sceneryGroup) + { + ResearchInsertSceneryGroupEntry(groupIndex, true); + + gSilentResearch = true; + ResearchResetCurrentItem(); + gSilentResearch = false; + } + + auto sceneryIntent = Intent(INTENT_ACTION_SET_DEFAULT_SCENERY_CONFIG); + ContextBroadcastIntent(&sceneryIntent); + + auto ridesIntent = Intent(INTENT_ACTION_REFRESH_NEW_RIDES); + ContextBroadcastIntent(&ridesIntent); + + gWindowUpdateTicks = 0; + GfxInvalidateScreen(); + console.WriteLine("Object file loaded."); } constexpr auto _objectTypeNames = std::to_array({ From 8e7bc2bc43b65fc327acbc7d0bb16386924a1f5b Mon Sep 17 00:00:00 2001 From: Gymnasiast Date: Thu, 23 Oct 2025 17:28:07 +0200 Subject: [PATCH 3/3] Close #21375: extend to allow loading JSON objects --- distribution/changelog.txt | 1 + src/openrct2/interface/InteractiveConsole.cpp | 29 +++++++++++++------ 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/distribution/changelog.txt b/distribution/changelog.txt index c1c79aaa31..059b1b227b 100644 --- a/distribution/changelog.txt +++ b/distribution/changelog.txt @@ -2,6 +2,7 @@ ------------------------------------------------------------------------ - Feature: [#25286] Footpath area dragging tool. - Feature: [#25379] Add an option to the command line screenshot function to draw debug segment heights. +- Improved: [#21375] The `load_object` console command now allows loading JSON objects. - Improved: [#25297] Paths on the ground in SV4/SC4 no longer block supports of the paths above. - Improved: [#25349] ‘Recent Messages’ window can now be fully themed. - Change: [#25089] Peep actions and animations that cause them to stop moving no longer trigger when they are on a level crossing. diff --git a/src/openrct2/interface/InteractiveConsole.cpp b/src/openrct2/interface/InteractiveConsole.cpp index 40ebc58f55..1b8a96615d 100644 --- a/src/openrct2/interface/InteractiveConsole.cpp +++ b/src/openrct2/interface/InteractiveConsole.cpp @@ -1057,22 +1057,32 @@ static void ConsoleCommandLoadObject(InteractiveConsole& console, const argument return; } - char name[9] = { 0 }; - std::fill_n(name, 8, ' '); - std::size_t i = 0; - for (const char* ch = argv[0].c_str(); *ch != '\0' && i < std::size(name) - 1; ch++) + auto& objectRepository = GetContext()->GetObjectRepository(); + auto objectName = argv[0]; + + // First, try and find a JSON object by this name + const ObjectRepositoryItem* ori = objectRepository.FindObject(objectName); + + // If this fails, try loading by DAT name + if (ori == nullptr) { - name[i++] = *ch; + char name[9] = { 0 }; + std::fill_n(name, 8, ' '); + std::size_t i = 0; + for (const char* ch = objectName.c_str(); *ch != '\0' && i < std::size(name) - 1; ch++) + { + name[i++] = *ch; + } + + ori = objectRepository.FindObjectLegacy(name); } - const ObjectRepositoryItem* ori = ObjectRepositoryFindObjectByName(name); if (ori == nullptr) { console.WriteLineError("Could not find the object."); return; } - const auto* entry = &ori->ObjectEntry; const auto* loadedObject = ObjectManagerGetLoadedObject(ObjectEntryDescriptor(*ori)); if (loadedObject != nullptr) { @@ -1080,7 +1090,8 @@ static void ConsoleCommandLoadObject(InteractiveConsole& console, const argument return; } - loadedObject = ObjectManagerLoadObject(entry); + auto& objectManager = OpenRCT2::GetContext()->GetObjectManager(); + loadedObject = objectManager.LoadRepositoryItem(*ori); if (loadedObject == nullptr) { console.WriteLineError("Unable to load object."); @@ -1088,7 +1099,7 @@ static void ConsoleCommandLoadObject(InteractiveConsole& console, const argument } auto groupIndex = ObjectManagerGetLoadedObjectEntryIndex(loadedObject); - ObjectType objectType = entry->GetType(); + ObjectType objectType = loadedObject->GetObjectType(); if (objectType == ObjectType::ride) { // Automatically research the ride so it's supported by the game.