diff --git a/distribution/openrct2.d.ts b/distribution/openrct2.d.ts
index 8838eb9888..b71ddc47c0 100644
--- a/distribution/openrct2.d.ts
+++ b/distribution/openrct2.d.ts
@@ -41,6 +41,10 @@ declare global {
var climate: Climate;
/** APIs for performance profiling. */
var profiler: Profiler;
+ /**
+ * APIs for getting, loading, and unloading objects.
+ */
+ var objectManager: ObjectManager;
/**
* APIs for creating and editing title sequences.
* These will only be available to clients that are not running headless mode.
@@ -217,9 +221,7 @@ declare global {
captureImage(options: CaptureOptions): void;
/**
- * Gets the loaded object at the given index.
- * @param type The object type.
- * @param index The index.
+ * @deprecated Use {@link ObjectManager.getObject} instead.
*/
getObject(type: ObjectType, index: number): LoadedImageObject;
getObject(type: "ride", index: number): RideObject;
@@ -231,6 +233,9 @@ declare global {
getObject(type: "scenery_group", index: number): SceneryGroupObject;
getObject(type: "music", index: number): LoadedObject;
+ /**
+ * @deprecated Use {@link ObjectManager.getAllObjects} instead.
+ */
getAllObjects(type: ObjectType): LoadedImageObject[];
getAllObjects(type: "ride"): RideObject[];
getAllObjects(type: "small_scenery"): SmallSceneryObject[];
@@ -4706,8 +4711,18 @@ declare global {
}
interface ObjectManager {
+ /**
+ * Gets all the objects that are installed and can be loaded into the park.
+ */
readonly installedObjects: InstalledObject[];
+ /**
+ * Gets the installed object with the given identifier, or null
+ * if the object was not found.
+ * @param identifier The object identifier.
+ */
+ getInstalledObject(identifier: string): InstalledObject | null;
+
/**
* Attempt to load the object into the current park at the given index for the object type.
* If an object already exists at the given index, that object will be unloaded and this object
@@ -4716,12 +4731,12 @@ declare global {
* @param index The index to load the object to. If not provided, an empty slot will be used.
* @returns The index of the loaded object.
*/
- load(identifier: string, index?: number): number;
+ load(identifier: string, index?: number): LoadedObject;
/**
* Attempt to load the given objects into the current park, given they are not already loaded.
*/
- load(identifiers: string[]): void;
+ load(identifiers: string[]): LoadedObject[];
/**
* Unloads the object, if loaded.
@@ -4741,5 +4756,34 @@ declare global {
* @param index The index of the slot to unload for the given type.
*/
unload(type: ObjectType, index: number): void;
+
+ /**
+ * Gets the loaded object at the given index.
+ * @param type The object type.
+ * @param index The index.
+ */
+ getObject(type: ObjectType, index: number): LoadedImageObject;
+ getObject(type: "ride", index: number): RideObject;
+ getObject(type: "small_scenery", index: number): SmallSceneryObject;
+ getObject(type: "large_scenery", index: number): LargeSceneryObject;
+ getObject(type: "wall", index: number): WallObject;
+ getObject(type: "footpath_addition", index: number): FootpathAdditionObject;
+ getObject(type: "banner", index: number): BannerObject;
+ getObject(type: "scenery_group", index: number): SceneryGroupObject;
+ getObject(type: "music", index: number): LoadedObject;
+
+ /**
+ * Gets all the currently loaded objects for a given object type.
+ * @param type The object type.
+ */
+ getAllObjects(type: ObjectType): LoadedImageObject[];
+ getAllObjects(type: "ride"): RideObject[];
+ getAllObjects(type: "small_scenery"): SmallSceneryObject[];
+ getAllObjects(type: "large_scenery"): LargeSceneryObject[];
+ getAllObjects(type: "wall"): WallObject[];
+ getAllObjects(type: "footpath_addition"): FootpathAdditionObject[];
+ getAllObjects(type: "banner"): BannerObject[];
+ getAllObjects(type: "scenery_group"): SceneryGroupObject[];
+ getAllObjects(type: "music"): LoadedObject[];
}
}
diff --git a/src/openrct2/libopenrct2.vcxproj b/src/openrct2/libopenrct2.vcxproj
index 51a3fccbb8..acf63387ce 100644
--- a/src/openrct2/libopenrct2.vcxproj
+++ b/src/openrct2/libopenrct2.vcxproj
@@ -509,6 +509,8 @@
+
+
@@ -986,6 +988,7 @@
+
diff --git a/src/openrct2/object/ObjectManager.cpp b/src/openrct2/object/ObjectManager.cpp
index fd79b209a7..516dce81ca 100644
--- a/src/openrct2/object/ObjectManager.cpp
+++ b/src/openrct2/object/ObjectManager.cpp
@@ -175,6 +175,12 @@ public:
return RepositoryItemToObject(ori);
}
+ Object* LoadObject(const ObjectEntryDescriptor& descriptor, ObjectEntryIndex slot) override
+ {
+ const ObjectRepositoryItem* ori = _objectRepository.FindObject(descriptor);
+ return RepositoryItemToObject(ori, slot);
+ }
+
void LoadObjects(const ObjectList& objectList) override
{
// Find all the required objects
diff --git a/src/openrct2/object/ObjectManager.h b/src/openrct2/object/ObjectManager.h
index 51b6b96757..ffa3c1e79a 100644
--- a/src/openrct2/object/ObjectManager.h
+++ b/src/openrct2/object/ObjectManager.h
@@ -36,6 +36,7 @@ struct IObjectManager
virtual Object* LoadObject(std::string_view identifier) abstract;
virtual Object* LoadObject(const RCTObjectEntry* entry) abstract;
virtual Object* LoadObject(const ObjectEntryDescriptor& descriptor) abstract;
+ virtual Object* LoadObject(const ObjectEntryDescriptor& descriptor, ObjectEntryIndex slot) abstract;
virtual void LoadObjects(const ObjectList& entries) abstract;
virtual void UnloadObjects(const std::vector& entries) abstract;
virtual void UnloadAllTransient() abstract;
diff --git a/src/openrct2/object/ObjectTypes.cpp b/src/openrct2/object/ObjectTypes.cpp
index e9988e7053..af545f07ed 100644
--- a/src/openrct2/object/ObjectTypes.cpp
+++ b/src/openrct2/object/ObjectTypes.cpp
@@ -7,7 +7,7 @@
* OpenRCT2 is licensed under the GNU General Public License version 3.
*****************************************************************************/
-#include "ObjectTypes.h"
+#include "Object.h"
#include
@@ -20,3 +20,11 @@ bool ObjectTypeIsIntransient(ObjectType type)
{
return std::find(IntransientObjectTypes.begin(), IntransientObjectTypes.end(), type) != std::end(IntransientObjectTypes);
}
+
+size_t GetObjectTypeLimit(ObjectType type)
+{
+ auto index = EnumValue(type);
+ if (index >= EnumValue(ObjectType::Count))
+ return 0;
+ return static_cast(object_entry_group_counts[index]);
+}
diff --git a/src/openrct2/object/ObjectTypes.h b/src/openrct2/object/ObjectTypes.h
index 05444663a9..12ab8a4dbc 100644
--- a/src/openrct2/object/ObjectTypes.h
+++ b/src/openrct2/object/ObjectTypes.h
@@ -78,3 +78,4 @@ constexpr std::array IntransientObjectTypes = { ObjectType::Scena
bool ObjectTypeIsTransient(ObjectType type);
bool ObjectTypeIsIntransient(ObjectType type);
+size_t GetObjectTypeLimit(ObjectType type);
diff --git a/src/openrct2/scripting/ScriptEngine.cpp b/src/openrct2/scripting/ScriptEngine.cpp
index f55c94b143..d2f7fdaa55 100644
--- a/src/openrct2/scripting/ScriptEngine.cpp
+++ b/src/openrct2/scripting/ScriptEngine.cpp
@@ -41,6 +41,7 @@
# include "bindings/network/ScSocket.hpp"
# include "bindings/object/ScInstalledObject.hpp"
# include "bindings/object/ScObject.hpp"
+# include "bindings/object/ScObjectManager.h"
# include "bindings/ride/ScRide.hpp"
# include "bindings/ride/ScRideStation.hpp"
# include "bindings/world/ScClimate.hpp"
@@ -404,6 +405,7 @@ void ScriptEngine::Initialise()
ScDisposable::Register(ctx);
ScMap::Register(ctx);
ScNetwork::Register(ctx);
+ ScObjectManager::Register(ctx);
ScInstalledObject::Register(ctx);
ScObject::Register(ctx);
ScSceneryObject::Register(ctx);
@@ -452,6 +454,7 @@ void ScriptEngine::Initialise()
dukglue_register_global(ctx, std::make_shared(ctx), "park");
dukglue_register_global(ctx, std::make_shared(ctx), "profiler");
dukglue_register_global(ctx, std::make_shared(), "scenario");
+ dukglue_register_global(ctx, std::make_shared(), "objectManager");
RegisterConstants();
diff --git a/src/openrct2/scripting/bindings/game/ScContext.hpp b/src/openrct2/scripting/bindings/game/ScContext.hpp
index d7f32223b7..1b3b411d8e 100644
--- a/src/openrct2/scripting/bindings/game/ScContext.hpp
+++ b/src/openrct2/scripting/bindings/game/ScContext.hpp
@@ -23,7 +23,7 @@
# include "../../ScriptEngine.h"
# include "../game/ScConfiguration.hpp"
# include "../game/ScDisposable.hpp"
-# include "../object/ScObject.hpp"
+# include "../object/ScObjectManager.h"
# include "../ride/ScTrackSegment.h"
# include
@@ -160,74 +160,18 @@ namespace OpenRCT2::Scripting
}
}
- static DukValue CreateScObject(duk_context* ctx, ObjectType type, int32_t index)
- {
- switch (type)
- {
- case ObjectType::Ride:
- return GetObjectAsDukValue(ctx, std::make_shared(type, index));
- case ObjectType::SmallScenery:
- return GetObjectAsDukValue(ctx, std::make_shared(type, index));
- case ObjectType::LargeScenery:
- return GetObjectAsDukValue(ctx, std::make_shared(type, index));
- case ObjectType::Walls:
- return GetObjectAsDukValue(ctx, std::make_shared(type, index));
- case ObjectType::PathBits:
- return GetObjectAsDukValue(ctx, std::make_shared(type, index));
- case ObjectType::Banners:
- return GetObjectAsDukValue(ctx, std::make_shared(type, index));
- case ObjectType::SceneryGroup:
- return GetObjectAsDukValue(ctx, std::make_shared(type, index));
- default:
- return GetObjectAsDukValue(ctx, std::make_shared(type, index));
- }
- }
-
DukValue getObject(const std::string& typez, int32_t index) const
{
- auto ctx = GetContext()->GetScriptEngine().GetContext();
- auto& objManager = GetContext()->GetObjectManager();
-
- auto type = ScObject::StringToObjectType(typez);
- if (type)
- {
- auto obj = objManager.GetLoadedObject(*type, index);
- if (obj != nullptr)
- {
- return CreateScObject(ctx, *type, index);
- }
- }
- else
- {
- duk_error(ctx, DUK_ERR_ERROR, "Invalid object type.");
- }
- return ToDuk(ctx, nullptr);
+ // deprecated function, moved to ObjectManager.getObject.
+ ScObjectManager objectManager;
+ return objectManager.getObject(typez, index);
}
std::vector getAllObjects(const std::string& typez) const
{
- auto ctx = GetContext()->GetScriptEngine().GetContext();
- auto& objManager = GetContext()->GetObjectManager();
-
- std::vector result;
- auto type = ScObject::StringToObjectType(typez);
- if (type)
- {
- auto count = object_entry_group_counts[EnumValue(*type)];
- for (int32_t i = 0; i < count; i++)
- {
- auto obj = objManager.GetLoadedObject(*type, i);
- if (obj != nullptr)
- {
- result.push_back(CreateScObject(ctx, *type, i));
- }
- }
- }
- else
- {
- duk_error(ctx, DUK_ERR_ERROR, "Invalid object type.");
- }
- return result;
+ // deprecated function, moved to ObjectManager.getAllObjects.
+ ScObjectManager objectManager;
+ return objectManager.getAllObjects(typez);
}
DukValue getTrackSegment(track_type_t type)
diff --git a/src/openrct2/scripting/bindings/object/ScObjectManager.cpp b/src/openrct2/scripting/bindings/object/ScObjectManager.cpp
new file mode 100644
index 0000000000..6ac67415b1
--- /dev/null
+++ b/src/openrct2/scripting/bindings/object/ScObjectManager.cpp
@@ -0,0 +1,270 @@
+#include "ScObjectManager.h"
+
+#include "../../../object/ObjectList.h"
+#include "../../../ride/RideData.h"
+#include "../../Duktape.hpp"
+#include "../../ScriptEngine.h"
+
+using namespace OpenRCT2;
+using namespace OpenRCT2::Scripting;
+
+void ScObjectManager::Register(duk_context* ctx)
+{
+ dukglue_register_property(ctx, &ScObjectManager::installedObjects_get, nullptr, "installedObjects");
+ dukglue_register_method(ctx, &ScObjectManager::load, "load");
+ dukglue_register_method(ctx, &ScObjectManager::unload, "unload");
+ dukglue_register_method(ctx, &ScObjectManager::getObject, "getObject");
+ dukglue_register_method(ctx, &ScObjectManager::getAllObjects, "getAllObjects");
+}
+
+std::vector> ScObjectManager::installedObjects_get() const
+{
+ std::vector> result;
+
+ auto context = GetContext();
+ auto& objectManager = context->GetObjectRepository();
+ auto count = objectManager.GetNumObjects();
+ for (size_t i = 0; i < count; i++)
+ {
+ auto installedObject = std::make_shared(i);
+ result.push_back(installedObject);
+ }
+
+ return result;
+}
+
+DukValue ScObjectManager::load(const DukValue& p1, const DukValue& p2)
+{
+ auto context = GetContext();
+ auto& scriptEngine = context->GetScriptEngine();
+ auto& objectRepository = context->GetObjectRepository();
+ auto& objectManager = context->GetObjectManager();
+ auto ctx = scriptEngine.GetContext();
+
+ if (p1.is_array())
+ {
+ // load(identifiers)
+ std::vector descriptors;
+ for (const auto& item : p1.as_array())
+ {
+ if (item.type() != DukValue::STRING)
+ throw DukException() << "Expected string for 'identifier'.";
+
+ const auto& identifier = item.as_string();
+ descriptors.emplace_back(identifier);
+ }
+
+ duk_push_array(ctx);
+ duk_uarridx_t index = 0;
+ for (const auto& descriptor : descriptors)
+ {
+ auto obj = objectManager.LoadObject(descriptor);
+ if (obj != nullptr)
+ {
+ MarkAsResearched(obj);
+ auto objIndex = objectManager.GetLoadedObjectEntryIndex(obj);
+ auto scLoadedObject = CreateScObject(scriptEngine.GetContext(), obj->GetObjectType(), objIndex);
+ scLoadedObject.push();
+ duk_put_prop_index(ctx, -2, index);
+ }
+ else
+ {
+ duk_push_null(ctx);
+ duk_put_prop_index(ctx, -2, index);
+ }
+ index++;
+ }
+ RefreshResearchedItems();
+ return DukValue::take_from_stack(ctx);
+ }
+ else
+ {
+ // load(identifier, index?)
+ if (p1.type() != DukValue::STRING)
+ throw DukException() << "Expected string for 'identifier'.";
+
+ const auto& identifier = p1.as_string();
+ ObjectEntryDescriptor descriptor(identifier);
+
+ auto installedObject = objectRepository.FindObject(descriptor);
+ if (installedObject != nullptr)
+ {
+ if (p2.type() != DukValue::UNDEFINED)
+ {
+ if (p2.type() != DukValue::NUMBER)
+ throw DukException() << "Expected number for 'index'.";
+
+ auto index = p2.as_int();
+ auto limit = GetObjectTypeLimit(installedObject->Type);
+ if (index < limit)
+ {
+ auto loadedObject = objectManager.GetLoadedObject(installedObject->Type, index);
+ if (loadedObject != nullptr)
+ {
+ objectManager.UnloadObjects({ loadedObject->GetDescriptor() });
+ }
+ auto obj = objectManager.LoadObject(descriptor, index);
+ if (obj != nullptr)
+ {
+ MarkAsResearched(obj);
+ RefreshResearchedItems();
+ auto objIndex = objectManager.GetLoadedObjectEntryIndex(obj);
+ return CreateScObject(scriptEngine.GetContext(), obj->GetObjectType(), objIndex);
+ }
+ }
+ }
+ else
+ {
+ auto obj = objectManager.LoadObject(descriptor);
+ if (obj != nullptr)
+ {
+ MarkAsResearched(obj);
+ RefreshResearchedItems();
+ auto objIndex = objectManager.GetLoadedObjectEntryIndex(obj);
+ return CreateScObject(scriptEngine.GetContext(), obj->GetObjectType(), objIndex);
+ }
+ }
+ }
+ }
+ return ToDuk(ctx, nullptr);
+}
+
+void ScObjectManager::unload(const DukValue& p1, const DukValue& p2)
+{
+ auto context = GetContext();
+ auto& objectManager = context->GetObjectManager();
+
+ if (p1.type() == DukValue::STRING)
+ {
+ const auto& szP1 = p1.as_string();
+ auto objType = ScObject::StringToObjectType(szP1);
+ if (objType)
+ {
+ // unload(type, index)
+ if (p2.type() != DukValue::NUMBER)
+ throw DukException() << "'index' is invalid.";
+
+ auto objIndex = p2.as_int();
+ auto obj = objectManager.GetLoadedObject(*objType, objIndex);
+ if (obj != nullptr)
+ {
+ objectManager.UnloadObjects({ obj->GetDescriptor() });
+ }
+ }
+ else
+ {
+ // unload(identifier)
+ objectManager.UnloadObjects({ ObjectEntryDescriptor(szP1) });
+ }
+ }
+ else if (p1.is_array())
+ {
+ // unload(identifiers)
+ auto identifiers = p1.as_array();
+ std::vector descriptors;
+ for (const auto& identifier : identifiers)
+ {
+ if (identifier.type() == DukValue::STRING)
+ {
+ descriptors.emplace_back(identifier.as_string());
+ }
+ }
+ objectManager.UnloadObjects(descriptors);
+ }
+}
+
+DukValue ScObjectManager::getObject(const std::string& typez, int32_t index) const
+{
+ auto ctx = GetContext()->GetScriptEngine().GetContext();
+ auto& objManager = GetContext()->GetObjectManager();
+
+ auto type = ScObject::StringToObjectType(typez);
+ if (type)
+ {
+ auto obj = objManager.GetLoadedObject(*type, index);
+ if (obj != nullptr)
+ {
+ return CreateScObject(ctx, *type, index);
+ }
+ }
+ else
+ {
+ duk_error(ctx, DUK_ERR_ERROR, "Invalid object type.");
+ }
+ return ToDuk(ctx, nullptr);
+}
+
+std::vector ScObjectManager::getAllObjects(const std::string& typez) const
+{
+ auto ctx = GetContext()->GetScriptEngine().GetContext();
+ auto& objManager = GetContext()->GetObjectManager();
+
+ std::vector result;
+ auto type = ScObject::StringToObjectType(typez);
+ if (type)
+ {
+ auto count = object_entry_group_counts[EnumValue(*type)];
+ for (int32_t i = 0; i < count; i++)
+ {
+ auto obj = objManager.GetLoadedObject(*type, i);
+ if (obj != nullptr)
+ {
+ result.push_back(CreateScObject(ctx, *type, i));
+ }
+ }
+ }
+ else
+ {
+ duk_error(ctx, DUK_ERR_ERROR, "Invalid object type.");
+ }
+ return result;
+}
+
+void ScObjectManager::MarkAsResearched(const Object* object)
+{
+ // Defaults selected items to researched (if in-game)
+ auto objectType = object->GetObjectType();
+ auto entryIndex = ObjectManagerGetLoadedObjectEntryIndex(object);
+ if (objectType == ObjectType::Ride)
+ {
+ const auto* rideEntry = GetRideEntryByIndex(entryIndex);
+ auto rideType = rideEntry->GetFirstNonNullRideType();
+ auto category = static_cast(GetRideTypeDescriptor(rideType).Category);
+ ResearchInsertRideEntry(rideType, entryIndex, category, true);
+ }
+ else if (objectType == ObjectType::SceneryGroup)
+ {
+ ResearchInsertSceneryGroupEntry(entryIndex, true);
+ }
+}
+
+void ScObjectManager::RefreshResearchedItems()
+{
+ // Same thing object selection window and inventions window does
+ gSilentResearch = true;
+ ResearchResetCurrentItem();
+ gSilentResearch = false;
+}
+
+DukValue ScObjectManager::CreateScObject(duk_context* ctx, ObjectType type, int32_t index)
+{
+ switch (type)
+ {
+ case ObjectType::Ride:
+ return GetObjectAsDukValue(ctx, std::make_shared(type, index));
+ case ObjectType::SmallScenery:
+ return GetObjectAsDukValue(ctx, std::make_shared(type, index));
+ case ObjectType::LargeScenery:
+ return GetObjectAsDukValue(ctx, std::make_shared(type, index));
+ case ObjectType::Walls:
+ return GetObjectAsDukValue(ctx, std::make_shared(type, index));
+ case ObjectType::PathBits:
+ return GetObjectAsDukValue(ctx, std::make_shared(type, index));
+ case ObjectType::Banners:
+ return GetObjectAsDukValue(ctx, std::make_shared(type, index));
+ case ObjectType::SceneryGroup:
+ return GetObjectAsDukValue(ctx, std::make_shared(type, index));
+ default:
+ return GetObjectAsDukValue(ctx, std::make_shared(type, index));
+ }
+}
diff --git a/src/openrct2/scripting/bindings/object/ScObjectManager.h b/src/openrct2/scripting/bindings/object/ScObjectManager.h
new file mode 100644
index 0000000000..f6ce560602
--- /dev/null
+++ b/src/openrct2/scripting/bindings/object/ScObjectManager.h
@@ -0,0 +1,43 @@
+/*****************************************************************************
+ * Copyright (c) 2014-2020 OpenRCT2 developers
+ *
+ * For a complete list of all authors, please refer to contributors.md
+ * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
+ *
+ * OpenRCT2 is licensed under the GNU General Public License version 3.
+ *****************************************************************************/
+
+#pragma once
+
+#ifdef ENABLE_SCRIPTING
+
+# include "../../Duktape.hpp"
+# include "../../ScriptEngine.h"
+# include "ScInstalledObject.hpp"
+# include "ScObject.hpp"
+
+# include
+
+namespace OpenRCT2::Scripting
+{
+ class ScObjectManager
+ {
+ public:
+ static void Register(duk_context* ctx);
+
+ std::vector> installedObjects_get() const;
+
+ DukValue load(const DukValue& p1, const DukValue& p2);
+ void unload(const DukValue& p1, const DukValue& p2);
+
+ DukValue getObject(const std::string& typez, int32_t index) const;
+ std::vector getAllObjects(const std::string& typez) const;
+
+ private:
+ static void MarkAsResearched(const Object* object);
+ static void RefreshResearchedItems();
+ static DukValue CreateScObject(duk_context* ctx, ObjectType type, int32_t index);
+ };
+} // namespace OpenRCT2::Scripting
+
+#endif