From 2729956cef830a34bdf760d6879cff863d5c108e Mon Sep 17 00:00:00 2001 From: Ted John Date: Mon, 17 Apr 2023 22:35:09 +0100 Subject: [PATCH 01/17] Add scenery group object interface --- distribution/openrct2.d.ts | 34 +++++++ src/openrct2/object/SceneryGroupObject.cpp | 5 + src/openrct2/object/SceneryGroupObject.h | 1 + src/openrct2/scripting/ScriptEngine.cpp | 1 + .../scripting/bindings/game/ScContext.hpp | 2 + .../scripting/bindings/object/ScObject.hpp | 95 ++++++++++++++++++- 6 files changed, 137 insertions(+), 1 deletion(-) diff --git a/distribution/openrct2.d.ts b/distribution/openrct2.d.ts index fd02debea6..b7415c2f80 100644 --- a/distribution/openrct2.d.ts +++ b/distribution/openrct2.d.ts @@ -1777,6 +1777,40 @@ declare global { readonly removalPrice: number; } + /** + * Represents the object definition of a scenery group. + */ + interface SceneryGroupObject extends LoadedImageObject { + /** + * The scenery items that belong to this scenery group. + */ + readonly items: SceneryGroupObjectItem[]; + } + + interface SceneryGroupObjectItem { + /** + * The JSON identifier of the object. + * Undefined if object only has a legacy identifier. + */ + identifier?: string; + + /** + * The DAT identifier of the object. + * Undefined if object only has a JSON identifier. + */ + legacyIdentifier?: string; + + /** + * The type of object + */ + type: ObjectType; + + /** + * The object index + */ + object: number | null; + } + /** * Represents a ride or stall within the park. */ diff --git a/src/openrct2/object/SceneryGroupObject.cpp b/src/openrct2/object/SceneryGroupObject.cpp index 7a6d32ca66..3eb485a4bf 100644 --- a/src/openrct2/object/SceneryGroupObject.cpp +++ b/src/openrct2/object/SceneryGroupObject.cpp @@ -243,3 +243,8 @@ uint16_t SceneryGroupObject::GetNumIncludedObjects() const { return static_cast(_items.size()); } + +const std::vector& SceneryGroupObject::GetItems() const +{ + return _items; +} diff --git a/src/openrct2/object/SceneryGroupObject.h b/src/openrct2/object/SceneryGroupObject.h index 9f1307889c..6c95bbb2a8 100644 --- a/src/openrct2/object/SceneryGroupObject.h +++ b/src/openrct2/object/SceneryGroupObject.h @@ -41,6 +41,7 @@ public: void SetRepositoryItem(ObjectRepositoryItem* item) const override; uint16_t GetNumIncludedObjects() const; + const std::vector& GetItems() const; private: static std::vector ReadItems(OpenRCT2::IStream* stream); diff --git a/src/openrct2/scripting/ScriptEngine.cpp b/src/openrct2/scripting/ScriptEngine.cpp index 8d9f9172a0..4893325724 100644 --- a/src/openrct2/scripting/ScriptEngine.cpp +++ b/src/openrct2/scripting/ScriptEngine.cpp @@ -405,6 +405,7 @@ void ScriptEngine::Initialise() ScNetwork::Register(ctx); ScObject::Register(ctx); ScSmallSceneryObject::Register(ctx); + ScSceneryGroupObject::Register(ctx); ScPark::Register(ctx); ScParkMessage::Register(ctx); ScPlayer::Register(ctx); diff --git a/src/openrct2/scripting/bindings/game/ScContext.hpp b/src/openrct2/scripting/bindings/game/ScContext.hpp index 73a2009e5f..ffd2dc72c3 100644 --- a/src/openrct2/scripting/bindings/game/ScContext.hpp +++ b/src/openrct2/scripting/bindings/game/ScContext.hpp @@ -168,6 +168,8 @@ namespace OpenRCT2::Scripting return GetObjectAsDukValue(ctx, std::make_shared(type, index)); case ObjectType::SmallScenery: 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/ScObject.hpp b/src/openrct2/scripting/bindings/object/ScObject.hpp index 9a51f7c2e5..a8978817da 100644 --- a/src/openrct2/scripting/bindings/object/ScObject.hpp +++ b/src/openrct2/scripting/bindings/object/ScObject.hpp @@ -15,6 +15,7 @@ # include "../../../common.h" # include "../../../object/ObjectManager.h" # include "../../../object/RideObject.h" +# include "../../../object/SceneryGroupObject.h" # include "../../../object/SmallSceneryObject.h" # include "../../Duktape.hpp" # include "../../ScriptEngine.h" @@ -58,7 +59,7 @@ namespace OpenRCT2::Scripting return static_cast(i); } } - return ObjectType::None; + return std::nullopt; } static std::string_view ObjectTypeToString(uint8_t type) @@ -865,6 +866,98 @@ namespace OpenRCT2::Scripting return static_cast(ScObject::GetObject()); } }; + + class ScSceneryGroupObject : public ScObject + { + public: + ScSceneryGroupObject(ObjectType type, int32_t index) + : ScObject(type, index) + { + } + + static void Register(duk_context* ctx) + { + dukglue_set_base_class(ctx); + dukglue_register_property(ctx, &ScSceneryGroupObject::items_get, nullptr, "items"); + } + + private: + DukValue items_get() const + { + auto* gameContext = GetContext(); + auto* ctx = gameContext->GetScriptEngine().GetContext(); + + duk_push_array(ctx); + + auto obj = GetObject(); + if (obj != nullptr) + { + auto& objManager = gameContext->GetObjectManager(); + + duk_uarridx_t index = 0; + auto& items = obj->GetItems(); + for (const auto& item : items) + { + auto objectIndex = objManager.GetLoadedObjectEntryIndex(item); + auto object = objManager.GetLoadedObject(item); + + DukObject dukObj(ctx); + if (item.Generation == ObjectGeneration::JSON) + { + dukObj.Set("identifier", item.Identifier); + if (object != nullptr) + { + auto legacyIdentifier = object->GetLegacyIdentifier(); + if (!legacyIdentifier.empty()) + { + dukObj.Set("legacyIdentifier", legacyIdentifier); + } + } + } + else + { + dukObj.Set("legacyIdentifier", item.Entry.GetName()); + if (object != nullptr) + { + auto identifier = object->GetIdentifier(); + if (!identifier.empty()) + { + dukObj.Set("identifier", identifier); + } + } + } + if (object != nullptr) + { + dukObj.Set("type", ObjectTypeToString(EnumValue(object->GetObjectType()))); + } + else + { + dukObj.Set("type", ObjectTypeToString(EnumValue(item.Type))); + } + if (objectIndex == OBJECT_ENTRY_INDEX_NULL) + { + dukObj.Set("object", nullptr); + } + else + { + dukObj.Set("object", objectIndex); + } + + dukObj.Take().push(); + duk_put_prop_index(ctx, -2, index); + index++; + } + } + + return DukValue::take_from_stack(ctx, -1); + } + + protected: + SceneryGroupObject* GetObject() const + { + return static_cast(ScObject::GetObject()); + } + }; } // namespace OpenRCT2::Scripting #endif From 1a73a0be681100e7b6f0d89b877d94aa5f51401b Mon Sep 17 00:00:00 2001 From: Ted John Date: Mon, 17 Apr 2023 23:46:15 +0100 Subject: [PATCH 02/17] Implement other scenery objects --- distribution/openrct2.d.ts | 84 +++++-- src/openrct2/scripting/ScriptEngine.cpp | 5 + .../scripting/bindings/game/ScContext.hpp | 8 + .../scripting/bindings/object/ScObject.hpp | 232 +++++++++++++----- 4 files changed, 240 insertions(+), 89 deletions(-) diff --git a/distribution/openrct2.d.ts b/distribution/openrct2.d.ts index b7415c2f80..c2d95d2cfc 100644 --- a/distribution/openrct2.d.ts +++ b/distribution/openrct2.d.ts @@ -225,6 +225,10 @@ declare global { getObject(type: "music", index: number): LoadedObject; 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): WallSceneryObject; + getObject(type: "footpath_addition", index: number): FootpathAdditionSceneryObject; + getObject(type: "banner", index: number): BannerSceneryObject; getAllObjects(type: ObjectType): LoadedImageObject[]; getAllObjects(type: "music"): LoadedObject[]; @@ -1752,10 +1756,46 @@ declare global { readonly numVerticalFramesOverride: number; } + interface SceneryObject extends LoadedImageObject { + /** + * A list of scenery groups this object belongs to. This may not contain any + * scenery groups that contain this object by default. This is typically + * used for custom objects to be part of existing scenery groups. + */ + readonly sceneryGroups: ObjectReference[]; + } + + /** + * Represents a reference to an object which may or may not be loaded. + */ + interface ObjectReference { + /** + * The JSON identifier of the object. + * Undefined if object only has a legacy identifier. + */ + identifier?: string; + + /** + * The DAT identifier of the object. + * Undefined if object only has a JSON identifier. + */ + legacyIdentifier?: string; + + /** + * The type of object + */ + type?: ObjectType; + + /** + * The object index + */ + object: number | null; + } + /** * Represents the object definition of a small scenery item such a tree. */ - interface SmallSceneryObject extends LoadedImageObject { + interface SmallSceneryObject extends SceneryObject { /** * Raw bit flags that describe characteristics of the scenery item. */ @@ -1777,6 +1817,22 @@ declare global { readonly removalPrice: number; } + interface LargeSceneryObject extends SceneryObject { + + } + + interface WallSceneryObject extends SceneryObject { + + } + + interface FootpathAdditionSceneryObject extends SceneryObject { + + } + + interface BannerSceneryObject extends SceneryObject { + + } + /** * Represents the object definition of a scenery group. */ @@ -1784,31 +1840,7 @@ declare global { /** * The scenery items that belong to this scenery group. */ - readonly items: SceneryGroupObjectItem[]; - } - - interface SceneryGroupObjectItem { - /** - * The JSON identifier of the object. - * Undefined if object only has a legacy identifier. - */ - identifier?: string; - - /** - * The DAT identifier of the object. - * Undefined if object only has a JSON identifier. - */ - legacyIdentifier?: string; - - /** - * The type of object - */ - type: ObjectType; - - /** - * The object index - */ - object: number | null; + readonly items: ObjectReference[]; } /** diff --git a/src/openrct2/scripting/ScriptEngine.cpp b/src/openrct2/scripting/ScriptEngine.cpp index 4893325724..f23a859ecf 100644 --- a/src/openrct2/scripting/ScriptEngine.cpp +++ b/src/openrct2/scripting/ScriptEngine.cpp @@ -404,7 +404,12 @@ void ScriptEngine::Initialise() ScMap::Register(ctx); ScNetwork::Register(ctx); ScObject::Register(ctx); + ScSceneryObject::Register(ctx); ScSmallSceneryObject::Register(ctx); + ScLargeSceneryObject::Register(ctx); + ScWallSceneryObject::Register(ctx); + ScFootpathAdditionSceneryObject::Register(ctx); + ScBannerSceneryObject::Register(ctx); ScSceneryGroupObject::Register(ctx); ScPark::Register(ctx); ScParkMessage::Register(ctx); diff --git a/src/openrct2/scripting/bindings/game/ScContext.hpp b/src/openrct2/scripting/bindings/game/ScContext.hpp index ffd2dc72c3..af02bf58e1 100644 --- a/src/openrct2/scripting/bindings/game/ScContext.hpp +++ b/src/openrct2/scripting/bindings/game/ScContext.hpp @@ -168,6 +168,14 @@ namespace OpenRCT2::Scripting 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: diff --git a/src/openrct2/scripting/bindings/object/ScObject.hpp b/src/openrct2/scripting/bindings/object/ScObject.hpp index a8978817da..5dce6c83f9 100644 --- a/src/openrct2/scripting/bindings/object/ScObject.hpp +++ b/src/openrct2/scripting/bindings/object/ScObject.hpp @@ -792,17 +792,114 @@ namespace OpenRCT2::Scripting } }; - class ScSmallSceneryObject : public ScObject + class ScSceneryObject : public ScObject { public: - ScSmallSceneryObject(ObjectType type, int32_t index) + ScSceneryObject(ObjectType type, int32_t index) : ScObject(type, index) { } static void Register(duk_context* ctx) { - dukglue_set_base_class(ctx); + dukglue_set_base_class(ctx); + dukglue_register_property(ctx, &ScSceneryObject::sceneryGroups_get, nullptr, "sceneryGroups"); + } + + private: + DukValue sceneryGroups_get() const + { + auto* gameContext = GetContext(); + auto* ctx = gameContext->GetScriptEngine().GetContext(); + + duk_push_array(ctx); + + auto obj = GetObject(); + if (obj != nullptr) + { + auto& scgDescriptor = obj->GetPrimarySceneryGroup(); + if (scgDescriptor.HasValue()) + { + auto dukScg = CreateObjectMetaReference(scgDescriptor); + dukScg.push(); + duk_put_prop_index(ctx, -2, 0); + } + } + + return DukValue::take_from_stack(ctx, -1); + } + + SceneryObject* GetObject() const + { + return static_cast(ScObject::GetObject()); + } + + public: + static DukValue CreateObjectMetaReference(const ObjectEntryDescriptor& descriptor) + { + auto* gameContext = GetContext(); + auto* ctx = gameContext->GetScriptEngine().GetContext(); + auto& objManager = gameContext->GetObjectManager(); + + auto objectIndex = objManager.GetLoadedObjectEntryIndex(descriptor); + auto object = objManager.GetLoadedObject(descriptor); + + DukObject dukObj(ctx); + if (descriptor.Generation == ObjectGeneration::JSON) + { + dukObj.Set("identifier", descriptor.Identifier); + if (object != nullptr) + { + auto legacyIdentifier = object->GetLegacyIdentifier(); + if (!legacyIdentifier.empty()) + { + dukObj.Set("legacyIdentifier", legacyIdentifier); + } + } + } + else + { + dukObj.Set("legacyIdentifier", descriptor.Entry.GetName()); + if (object != nullptr) + { + auto identifier = object->GetIdentifier(); + if (!identifier.empty()) + { + dukObj.Set("identifier", identifier); + } + } + } + if (object != nullptr) + { + dukObj.Set("type", ObjectTypeToString(EnumValue(object->GetObjectType()))); + } + else + { + dukObj.Set("type", ObjectTypeToString(EnumValue(descriptor.Type))); + } + if (objectIndex == OBJECT_ENTRY_INDEX_NULL) + { + dukObj.Set("object", nullptr); + } + else + { + dukObj.Set("object", objectIndex); + } + return dukObj.Take(); + } + }; + + class ScSmallSceneryObject : public ScSceneryObject + { + public: + ScSmallSceneryObject(ObjectType type, int32_t index) + : ScSceneryObject(type, index) + { + } + + static void Register(duk_context* ctx) + { + dukglue_set_base_class(ctx); dukglue_register_property(ctx, &ScSmallSceneryObject::flags_get, nullptr, "flags"); dukglue_register_property(ctx, &ScSmallSceneryObject::height_get, nullptr, "height"); dukglue_register_property(ctx, &ScSmallSceneryObject::price_get, nullptr, "price"); @@ -820,16 +917,6 @@ namespace OpenRCT2::Scripting return 0; } - uint8_t height_get() const - { - auto sceneryEntry = GetLegacyData(); - if (sceneryEntry != nullptr) - { - return sceneryEntry->height; - } - return 0; - } - uint8_t price_get() const { auto sceneryEntry = GetLegacyData(); @@ -840,6 +927,16 @@ namespace OpenRCT2::Scripting return 0; } + uint8_t height_get() const + { + auto sceneryEntry = GetLegacyData(); + if (sceneryEntry != nullptr) + { + return sceneryEntry->height; + } + return 0; + } + uint8_t removalPrice_get() const { auto sceneryEntry = GetLegacyData(); @@ -867,6 +964,62 @@ namespace OpenRCT2::Scripting } }; + class ScLargeSceneryObject : public ScSceneryObject + { + public: + ScLargeSceneryObject(ObjectType type, int32_t index) + : ScSceneryObject(type, index) + { + } + + static void Register(duk_context* ctx) + { + dukglue_set_base_class(ctx); + } + }; + + class ScWallSceneryObject : public ScSceneryObject + { + public: + ScWallSceneryObject(ObjectType type, int32_t index) + : ScSceneryObject(type, index) + { + } + + static void Register(duk_context* ctx) + { + dukglue_set_base_class(ctx); + } + }; + + class ScFootpathAdditionSceneryObject : public ScSceneryObject + { + public: + ScFootpathAdditionSceneryObject(ObjectType type, int32_t index) + : ScSceneryObject(type, index) + { + } + + static void Register(duk_context* ctx) + { + dukglue_set_base_class(ctx); + } + }; + + class ScBannerSceneryObject : public ScSceneryObject + { + public: + ScBannerSceneryObject(ObjectType type, int32_t index) + : ScSceneryObject(type, index) + { + } + + static void Register(duk_context* ctx) + { + dukglue_set_base_class(ctx); + } + }; + class ScSceneryGroupObject : public ScObject { public: @@ -884,66 +1037,19 @@ namespace OpenRCT2::Scripting private: DukValue items_get() const { - auto* gameContext = GetContext(); - auto* ctx = gameContext->GetScriptEngine().GetContext(); + auto* ctx = GetContext()->GetScriptEngine().GetContext(); duk_push_array(ctx); auto obj = GetObject(); if (obj != nullptr) { - auto& objManager = gameContext->GetObjectManager(); - duk_uarridx_t index = 0; auto& items = obj->GetItems(); for (const auto& item : items) { - auto objectIndex = objManager.GetLoadedObjectEntryIndex(item); - auto object = objManager.GetLoadedObject(item); - - DukObject dukObj(ctx); - if (item.Generation == ObjectGeneration::JSON) - { - dukObj.Set("identifier", item.Identifier); - if (object != nullptr) - { - auto legacyIdentifier = object->GetLegacyIdentifier(); - if (!legacyIdentifier.empty()) - { - dukObj.Set("legacyIdentifier", legacyIdentifier); - } - } - } - else - { - dukObj.Set("legacyIdentifier", item.Entry.GetName()); - if (object != nullptr) - { - auto identifier = object->GetIdentifier(); - if (!identifier.empty()) - { - dukObj.Set("identifier", identifier); - } - } - } - if (object != nullptr) - { - dukObj.Set("type", ObjectTypeToString(EnumValue(object->GetObjectType()))); - } - else - { - dukObj.Set("type", ObjectTypeToString(EnumValue(item.Type))); - } - if (objectIndex == OBJECT_ENTRY_INDEX_NULL) - { - dukObj.Set("object", nullptr); - } - else - { - dukObj.Set("object", objectIndex); - } - - dukObj.Take().push(); + auto dukItem = ScSceneryObject::CreateObjectMetaReference(item); + dukItem.push(); duk_put_prop_index(ctx, -2, index); index++; } From b3fc7fcf185ec707d5608478b19009d00dbf2d90 Mon Sep 17 00:00:00 2001 From: Ted John Date: Mon, 17 Apr 2023 23:52:34 +0100 Subject: [PATCH 03/17] Update changelog --- distribution/changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/distribution/changelog.txt b/distribution/changelog.txt index 249c5d7f71..db04de607b 100644 --- a/distribution/changelog.txt +++ b/distribution/changelog.txt @@ -7,6 +7,7 @@ - Feature: [#19446] Add new color options to color dropdown. - Feature: [#19547] Add large sloped turns to hybrid coaster and single rail coaster. - Feature: [#19930] Add plugin APIs for research. +- Feature: [#19979] Add plugin API for scenery groups. - Feature: [OpenMusic#25] Added Prehistoric ride music style. - Improved: [#17739] Raise water and land height limits to 254 units/182m/600ft. - Improved: [#18490] Reduce guests walking through trains on level crossing next to station. From 74fc4d3666586d03c627adf71772ea3a1c30e32e Mon Sep 17 00:00:00 2001 From: Ted John Date: Mon, 17 Apr 2023 23:53:47 +0100 Subject: [PATCH 04/17] Revert re order of small scenery function --- .../scripting/bindings/object/ScObject.hpp | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/openrct2/scripting/bindings/object/ScObject.hpp b/src/openrct2/scripting/bindings/object/ScObject.hpp index 5dce6c83f9..55f8613349 100644 --- a/src/openrct2/scripting/bindings/object/ScObject.hpp +++ b/src/openrct2/scripting/bindings/object/ScObject.hpp @@ -917,16 +917,6 @@ namespace OpenRCT2::Scripting return 0; } - uint8_t price_get() const - { - auto sceneryEntry = GetLegacyData(); - if (sceneryEntry != nullptr) - { - return sceneryEntry->price; - } - return 0; - } - uint8_t height_get() const { auto sceneryEntry = GetLegacyData(); @@ -937,6 +927,16 @@ namespace OpenRCT2::Scripting return 0; } + uint8_t price_get() const + { + auto sceneryEntry = GetLegacyData(); + if (sceneryEntry != nullptr) + { + return sceneryEntry->price; + } + return 0; + } + uint8_t removalPrice_get() const { auto sceneryEntry = GetLegacyData(); From bf3c6964be6af8033e7c5a4b0e16b1c5ebe772dd Mon Sep 17 00:00:00 2001 From: Ted John Date: Tue, 18 Apr 2023 21:55:50 +0100 Subject: [PATCH 05/17] Remove scenery from object type names --- distribution/openrct2.d.ts | 21 ++++++++++--------- src/openrct2/scripting/ScriptEngine.cpp | 6 +++--- .../scripting/bindings/game/ScContext.hpp | 6 +++--- .../scripting/bindings/object/ScObject.hpp | 18 ++++++++-------- 4 files changed, 26 insertions(+), 25 deletions(-) diff --git a/distribution/openrct2.d.ts b/distribution/openrct2.d.ts index c2d95d2cfc..dad22d66c2 100644 --- a/distribution/openrct2.d.ts +++ b/distribution/openrct2.d.ts @@ -226,9 +226,10 @@ declare global { 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): WallSceneryObject; - getObject(type: "footpath_addition", index: number): FootpathAdditionSceneryObject; - getObject(type: "banner", index: number): BannerSceneryObject; + 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; getAllObjects(type: ObjectType): LoadedImageObject[]; getAllObjects(type: "music"): LoadedObject[]; @@ -1773,23 +1774,23 @@ declare global { * The JSON identifier of the object. * Undefined if object only has a legacy identifier. */ - identifier?: string; + readonly identifier?: string; /** * The DAT identifier of the object. * Undefined if object only has a JSON identifier. */ - legacyIdentifier?: string; + readonly legacyIdentifier?: string; /** * The type of object */ - type?: ObjectType; + readonly type?: ObjectType; /** * The object index */ - object: number | null; + readonly object: number | null; } /** @@ -1821,15 +1822,15 @@ declare global { } - interface WallSceneryObject extends SceneryObject { + interface WallObject extends SceneryObject { } - interface FootpathAdditionSceneryObject extends SceneryObject { + interface FootpathAdditionObject extends SceneryObject { } - interface BannerSceneryObject extends SceneryObject { + interface BannerObject extends SceneryObject { } diff --git a/src/openrct2/scripting/ScriptEngine.cpp b/src/openrct2/scripting/ScriptEngine.cpp index f23a859ecf..d98ec01034 100644 --- a/src/openrct2/scripting/ScriptEngine.cpp +++ b/src/openrct2/scripting/ScriptEngine.cpp @@ -407,9 +407,9 @@ void ScriptEngine::Initialise() ScSceneryObject::Register(ctx); ScSmallSceneryObject::Register(ctx); ScLargeSceneryObject::Register(ctx); - ScWallSceneryObject::Register(ctx); - ScFootpathAdditionSceneryObject::Register(ctx); - ScBannerSceneryObject::Register(ctx); + ScWallObject::Register(ctx); + ScFootpathAdditionObject::Register(ctx); + ScBannerObject::Register(ctx); ScSceneryGroupObject::Register(ctx); ScPark::Register(ctx); ScParkMessage::Register(ctx); diff --git a/src/openrct2/scripting/bindings/game/ScContext.hpp b/src/openrct2/scripting/bindings/game/ScContext.hpp index af02bf58e1..d7f32223b7 100644 --- a/src/openrct2/scripting/bindings/game/ScContext.hpp +++ b/src/openrct2/scripting/bindings/game/ScContext.hpp @@ -171,11 +171,11 @@ namespace OpenRCT2::Scripting case ObjectType::LargeScenery: return GetObjectAsDukValue(ctx, std::make_shared(type, index)); case ObjectType::Walls: - return GetObjectAsDukValue(ctx, std::make_shared(type, index)); + return GetObjectAsDukValue(ctx, std::make_shared(type, index)); case ObjectType::PathBits: - return GetObjectAsDukValue(ctx, std::make_shared(type, index)); + return GetObjectAsDukValue(ctx, std::make_shared(type, index)); case ObjectType::Banners: - return GetObjectAsDukValue(ctx, std::make_shared(type, index)); + return GetObjectAsDukValue(ctx, std::make_shared(type, index)); case ObjectType::SceneryGroup: return GetObjectAsDukValue(ctx, std::make_shared(type, index)); default: diff --git a/src/openrct2/scripting/bindings/object/ScObject.hpp b/src/openrct2/scripting/bindings/object/ScObject.hpp index 55f8613349..bef16a38aa 100644 --- a/src/openrct2/scripting/bindings/object/ScObject.hpp +++ b/src/openrct2/scripting/bindings/object/ScObject.hpp @@ -978,45 +978,45 @@ namespace OpenRCT2::Scripting } }; - class ScWallSceneryObject : public ScSceneryObject + class ScWallObject : public ScSceneryObject { public: - ScWallSceneryObject(ObjectType type, int32_t index) + ScWallObject(ObjectType type, int32_t index) : ScSceneryObject(type, index) { } static void Register(duk_context* ctx) { - dukglue_set_base_class(ctx); + dukglue_set_base_class(ctx); } }; - class ScFootpathAdditionSceneryObject : public ScSceneryObject + class ScFootpathAdditionObject : public ScSceneryObject { public: - ScFootpathAdditionSceneryObject(ObjectType type, int32_t index) + ScFootpathAdditionObject(ObjectType type, int32_t index) : ScSceneryObject(type, index) { } static void Register(duk_context* ctx) { - dukglue_set_base_class(ctx); + dukglue_set_base_class(ctx); } }; - class ScBannerSceneryObject : public ScSceneryObject + class ScBannerObject : public ScSceneryObject { public: - ScBannerSceneryObject(ObjectType type, int32_t index) + ScBannerObject(ObjectType type, int32_t index) : ScSceneryObject(type, index) { } static void Register(duk_context* ctx) { - dukglue_set_base_class(ctx); + dukglue_set_base_class(ctx); } }; From 58dc3f193b60b3fb160ce853d15f448dc5c28071 Mon Sep 17 00:00:00 2001 From: Ted John Date: Thu, 20 Apr 2023 21:50:29 +0100 Subject: [PATCH 06/17] Use simple identifiers --- src/openrct2/object/Object.cpp | 18 ++++ src/openrct2/object/Object.h | 1 + .../scripting/bindings/object/ScObject.hpp | 94 ++++--------------- 3 files changed, 35 insertions(+), 78 deletions(-) diff --git a/src/openrct2/object/Object.cpp b/src/openrct2/object/Object.cpp index 94a3aff0e9..d2a0bacfd0 100644 --- a/src/openrct2/object/Object.cpp +++ b/src/openrct2/object/Object.cpp @@ -80,6 +80,24 @@ std::string_view ObjectEntryDescriptor::GetName() const return Generation == ObjectGeneration::JSON ? Identifier : Entry.GetName(); } +std::string ObjectEntryDescriptor::ToString() const +{ + if (Generation == ObjectGeneration::DAT) + { + char buffer[32]; + std::snprintf(&buffer[0], 9, "%08X", Entry.flags); + buffer[8] = '|'; + std::memcpy(&buffer[9], Entry.name, 8); + buffer[17] = '|'; + std::snprintf(&buffer[18], 9, "%8X", Entry.checksum); + return std::string(buffer); + } + else + { + return std::string(GetName()); + } +} + bool ObjectEntryDescriptor::operator==(const ObjectEntryDescriptor& rhs) const { if (Generation != rhs.Generation) diff --git a/src/openrct2/object/Object.h b/src/openrct2/object/Object.h index 71bda63dc3..21a715628e 100644 --- a/src/openrct2/object/Object.h +++ b/src/openrct2/object/Object.h @@ -139,6 +139,7 @@ struct ObjectEntryDescriptor bool HasValue() const; ObjectType GetType() const; std::string_view GetName() const; + std::string ToString() const; bool operator==(const ObjectEntryDescriptor& rhs) const; bool operator!=(const ObjectEntryDescriptor& rhs) const; diff --git a/src/openrct2/scripting/bindings/object/ScObject.hpp b/src/openrct2/scripting/bindings/object/ScObject.hpp index bef16a38aa..5cb632cdb4 100644 --- a/src/openrct2/scripting/bindings/object/ScObject.hpp +++ b/src/openrct2/scripting/bindings/object/ScObject.hpp @@ -104,7 +104,14 @@ namespace OpenRCT2::Scripting auto obj = GetObject(); if (obj != nullptr) { - return std::string(obj->GetIdentifier()); + if (obj->GetGeneration() == ObjectGeneration::DAT) + { + return obj->GetDescriptor().ToString(); + } + else + { + return std::string(obj->GetIdentifier()); + } } return {}; } @@ -807,86 +814,25 @@ namespace OpenRCT2::Scripting } private: - DukValue sceneryGroups_get() const + std::vector sceneryGroups_get() const { - auto* gameContext = GetContext(); - auto* ctx = gameContext->GetScriptEngine().GetContext(); - - duk_push_array(ctx); - + std::vector result; auto obj = GetObject(); if (obj != nullptr) { auto& scgDescriptor = obj->GetPrimarySceneryGroup(); if (scgDescriptor.HasValue()) { - auto dukScg = CreateObjectMetaReference(scgDescriptor); - dukScg.push(); - duk_put_prop_index(ctx, -2, 0); + result.push_back(scgDescriptor.ToString()); } } - - return DukValue::take_from_stack(ctx, -1); + return result; } SceneryObject* GetObject() const { return static_cast(ScObject::GetObject()); } - - public: - static DukValue CreateObjectMetaReference(const ObjectEntryDescriptor& descriptor) - { - auto* gameContext = GetContext(); - auto* ctx = gameContext->GetScriptEngine().GetContext(); - auto& objManager = gameContext->GetObjectManager(); - - auto objectIndex = objManager.GetLoadedObjectEntryIndex(descriptor); - auto object = objManager.GetLoadedObject(descriptor); - - DukObject dukObj(ctx); - if (descriptor.Generation == ObjectGeneration::JSON) - { - dukObj.Set("identifier", descriptor.Identifier); - if (object != nullptr) - { - auto legacyIdentifier = object->GetLegacyIdentifier(); - if (!legacyIdentifier.empty()) - { - dukObj.Set("legacyIdentifier", legacyIdentifier); - } - } - } - else - { - dukObj.Set("legacyIdentifier", descriptor.Entry.GetName()); - if (object != nullptr) - { - auto identifier = object->GetIdentifier(); - if (!identifier.empty()) - { - dukObj.Set("identifier", identifier); - } - } - } - if (object != nullptr) - { - dukObj.Set("type", ObjectTypeToString(EnumValue(object->GetObjectType()))); - } - else - { - dukObj.Set("type", ObjectTypeToString(EnumValue(descriptor.Type))); - } - if (objectIndex == OBJECT_ENTRY_INDEX_NULL) - { - dukObj.Set("object", nullptr); - } - else - { - dukObj.Set("object", objectIndex); - } - return dukObj.Take(); - } }; class ScSmallSceneryObject : public ScSceneryObject @@ -1035,27 +981,19 @@ namespace OpenRCT2::Scripting } private: - DukValue items_get() const + std::vector items_get() const { - auto* ctx = GetContext()->GetScriptEngine().GetContext(); - - duk_push_array(ctx); - + std::vector result; auto obj = GetObject(); if (obj != nullptr) { - duk_uarridx_t index = 0; auto& items = obj->GetItems(); for (const auto& item : items) { - auto dukItem = ScSceneryObject::CreateObjectMetaReference(item); - dukItem.push(); - duk_put_prop_index(ctx, -2, index); - index++; + result.push_back(std::move(item.ToString())); } } - - return DukValue::take_from_stack(ctx, -1); + return result; } protected: From d3a65443f2a98700e6a8f6e480981301cc15504a Mon Sep 17 00:00:00 2001 From: Ted John Date: Fri, 21 Apr 2023 21:20:17 +0100 Subject: [PATCH 07/17] Increment plugin API version --- src/openrct2/scripting/ScriptEngine.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/openrct2/scripting/ScriptEngine.h b/src/openrct2/scripting/ScriptEngine.h index ac870caa85..30eb5ed5bd 100644 --- a/src/openrct2/scripting/ScriptEngine.h +++ b/src/openrct2/scripting/ScriptEngine.h @@ -47,7 +47,7 @@ namespace OpenRCT2 namespace OpenRCT2::Scripting { - static constexpr int32_t OPENRCT2_PLUGIN_API_VERSION = 77; + static constexpr int32_t OPENRCT2_PLUGIN_API_VERSION = 78; // Versions marking breaking changes. static constexpr int32_t API_VERSION_33_PEEP_DEPRECATION = 33; From f1ac4363ce60f80c805aec3f455bc034f8ecc802 Mon Sep 17 00:00:00 2001 From: Ted John Date: Sat, 22 Apr 2023 08:52:21 +0100 Subject: [PATCH 08/17] Fix openrct2.d.ts --- distribution/openrct2.d.ts | 51 ++++++------------- .../scripting/bindings/object/ScObject.hpp | 2 +- 2 files changed, 16 insertions(+), 37 deletions(-) diff --git a/distribution/openrct2.d.ts b/distribution/openrct2.d.ts index dad22d66c2..364169e59c 100644 --- a/distribution/openrct2.d.ts +++ b/distribution/openrct2.d.ts @@ -222,7 +222,6 @@ declare global { * @param index The index. */ getObject(type: ObjectType, index: number): LoadedImageObject; - getObject(type: "music", index: number): LoadedObject; getObject(type: "ride", index: number): RideObject; getObject(type: "small_scenery", index: number): SmallSceneryObject; getObject(type: "large_scenery", index: number): LargeSceneryObject; @@ -230,10 +229,17 @@ declare global { 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; getAllObjects(type: ObjectType): LoadedImageObject[]; - getAllObjects(type: "music"): LoadedObject[]; 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[]; /** * Gets the {@link TrackSegment} for the given type. @@ -1763,34 +1769,7 @@ declare global { * scenery groups that contain this object by default. This is typically * used for custom objects to be part of existing scenery groups. */ - readonly sceneryGroups: ObjectReference[]; - } - - /** - * Represents a reference to an object which may or may not be loaded. - */ - interface ObjectReference { - /** - * The JSON identifier of the object. - * Undefined if object only has a legacy identifier. - */ - readonly identifier?: string; - - /** - * The DAT identifier of the object. - * Undefined if object only has a JSON identifier. - */ - readonly legacyIdentifier?: string; - - /** - * The type of object - */ - readonly type?: ObjectType; - - /** - * The object index - */ - readonly object: number | null; + readonly sceneryGroups: string[]; } /** @@ -1841,7 +1820,7 @@ declare global { /** * The scenery items that belong to this scenery group. */ - readonly items: ObjectReference[]; + readonly items: string[]; } /** @@ -2404,11 +2383,11 @@ declare global { * The current tilt of the car in the X/Y axis. */ bankRotation: number; - - /** - * Whether the car sprite is reversed or not. - */ - isReversed: boolean; + + /** + * Whether the car sprite is reversed or not. + */ + isReversed: boolean; /** * The colour of the car. diff --git a/src/openrct2/scripting/bindings/object/ScObject.hpp b/src/openrct2/scripting/bindings/object/ScObject.hpp index 5cb632cdb4..589d71c138 100644 --- a/src/openrct2/scripting/bindings/object/ScObject.hpp +++ b/src/openrct2/scripting/bindings/object/ScObject.hpp @@ -990,7 +990,7 @@ namespace OpenRCT2::Scripting auto& items = obj->GetItems(); for (const auto& item : items) { - result.push_back(std::move(item.ToString())); + result.push_back(item.ToString()); } } return result; From 96ef163d5106e9684f466e124a0b7fa01d0be228 Mon Sep 17 00:00:00 2001 From: Ted John Date: Mon, 11 Apr 2022 01:34:27 +0100 Subject: [PATCH 09/17] Add new APIs to openrct2.d.ts --- distribution/openrct2.d.ts | 84 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/distribution/openrct2.d.ts b/distribution/openrct2.d.ts index 364169e59c..f5c95d0255 100644 --- a/distribution/openrct2.d.ts +++ b/distribution/openrct2.d.ts @@ -1597,10 +1597,94 @@ declare global { removeElement(index: number): void; } + type ObjectSourceGame = + "rct1" | + "added_attractions" | + "loopy_landscapes" | + "rct2" | + "wacky_worlds" | + "time_twister" | + "custom" | + "openrct2_official"; + + type ObjectGeneration = "dat" | "json"; + + /** + * Represents an installed OpenRCT2 object which may or may not be currently loaded into the park. + */ + interface InstalledObject { + /** + * The full path of the object file. + */ + readonly path: string; + + /** + * Whether the object is an original .DAT file, or a .parkobj / .json file. + */ + readonly generation: ObjectGeneration; + + /** + * The object type. + */ + readonly type: ObjectType; + + /** + * The original game or expansion pack this object first appeared in. + */ + readonly sourceGame: ObjectSourceGame; + + /** + * The unique identifier of the object, e.g. "rct2.burgb". + * Only JSON objects will have an identifier. + */ + readonly identifier: string; + + /** + * The original unique identifier of the object, e.g. "BURGB ". + * This may have trailing spaces if the name is shorter than 8 characters. + * Only .DAT objects or JSON objects based on .DAT objects will have legacy identifiers. + */ + readonly legacyIdentifier: string; + + /** + * The object version, e.g. "1.5.2-pre". + */ + readonly version: string; + + /** + * Gets the list of authors for the object. + */ + readonly authors: string[]; + + /** + * The name in the user's current language. + */ + readonly name: string; + + /** + * 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 + * will replace it, providing the object type is the same. + * @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(index?: number): number; + + /** + * Unloads the object, if loaded. + */ + unload(): void; + } + /** * Represents the definition of a loaded object (.DAT or .json) such as ride type or scenery item. */ interface LoadedObject { + /** + * Gets a reference to the installed object. + */ + readonly installedObject: InstalledObject; + /** * The object type. */ From f95472c70ae07a89259f96b64ea6da9ebc730db0 Mon Sep 17 00:00:00 2001 From: Ted John Date: Mon, 11 Apr 2022 23:18:44 +0100 Subject: [PATCH 10/17] Add API for installed objects --- distribution/openrct2.d.ts | 16 +- src/openrct2/scripting/ScriptEngine.cpp | 2 + .../bindings/object/ScInstalledObject.hpp | 189 ++++++++++++++++++ .../scripting/bindings/object/ScObject.hpp | 39 ++-- 4 files changed, 207 insertions(+), 39 deletions(-) create mode 100644 src/openrct2/scripting/bindings/object/ScInstalledObject.hpp diff --git a/distribution/openrct2.d.ts b/distribution/openrct2.d.ts index f5c95d0255..cf53310bf8 100644 --- a/distribution/openrct2.d.ts +++ b/distribution/openrct2.d.ts @@ -1631,7 +1631,7 @@ declare global { /** * The original game or expansion pack this object first appeared in. */ - readonly sourceGame: ObjectSourceGame; + readonly sourceGames: ObjectSourceGame[]; /** * The unique identifier of the object, e.g. "rct2.burgb". @@ -1660,20 +1660,6 @@ declare global { * The name in the user's current language. */ readonly name: string; - - /** - * 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 - * will replace it, providing the object type is the same. - * @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(index?: number): number; - - /** - * Unloads the object, if loaded. - */ - unload(): void; } /** diff --git a/src/openrct2/scripting/ScriptEngine.cpp b/src/openrct2/scripting/ScriptEngine.cpp index d98ec01034..f55c94b143 100644 --- a/src/openrct2/scripting/ScriptEngine.cpp +++ b/src/openrct2/scripting/ScriptEngine.cpp @@ -39,6 +39,7 @@ # include "bindings/network/ScPlayer.hpp" # include "bindings/network/ScPlayerGroup.hpp" # include "bindings/network/ScSocket.hpp" +# include "bindings/object/ScInstalledObject.hpp" # include "bindings/object/ScObject.hpp" # include "bindings/ride/ScRide.hpp" # include "bindings/ride/ScRideStation.hpp" @@ -403,6 +404,7 @@ void ScriptEngine::Initialise() ScDisposable::Register(ctx); ScMap::Register(ctx); ScNetwork::Register(ctx); + ScInstalledObject::Register(ctx); ScObject::Register(ctx); ScSceneryObject::Register(ctx); ScSmallSceneryObject::Register(ctx); diff --git a/src/openrct2/scripting/bindings/object/ScInstalledObject.hpp b/src/openrct2/scripting/bindings/object/ScInstalledObject.hpp new file mode 100644 index 0000000000..eb8178d33b --- /dev/null +++ b/src/openrct2/scripting/bindings/object/ScInstalledObject.hpp @@ -0,0 +1,189 @@ +/***************************************************************************** + * 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 "../../../Context.h" +# include "../../../common.h" +# include "../../../object/ObjectRepository.h" +# include "../../Duktape.hpp" +# include "../../ScriptEngine.h" + +# include + +namespace OpenRCT2::Scripting +{ + inline std::string_view ObjectTypeToString(uint8_t type) + { + static constexpr std::string_view Types[] = { + "ride", + "small_scenery", + "large_scenery", + "wall", + "banner", + "footpath", + "footpath_addition", + "scenery_group", + "park_entrance", + "water", + "stex", + "terrain_surface", + "terrain_edge", + "station", + "music", + "footpath_surface", + "footpath_railings", + }; + if (type >= std::size(Types)) + return "unknown"; + return Types[type]; + } + + inline std::string_view ObjectSourceGameToString(ObjectSourceGame sourceGame) + { + static constexpr std::string_view values[] = { "custom", "wacky_worlds", "time_twister", "openrct2_official", + "rct1", "added_attractions", "loopy_landscapes", "unknown", + "rct2" }; + if (EnumValue(sourceGame) >= std::size(values)) + return "unknown"; + return values[EnumValue(sourceGame)]; + } + + class ScInstalledObject + { + protected: + size_t _index{}; + + public: + ScInstalledObject(size_t index) + : _index(index) + { + } + + static void Register(duk_context* ctx) + { + dukglue_register_property(ctx, &ScInstalledObject::path_get, nullptr, "path"); + dukglue_register_property(ctx, &ScInstalledObject::generation_get, nullptr, "generation"); + dukglue_register_property(ctx, &ScInstalledObject::identifier_get, nullptr, "identifier"); + dukglue_register_property(ctx, &ScInstalledObject::type_get, nullptr, "type"); + dukglue_register_property(ctx, &ScInstalledObject::sourceGames_get, nullptr, "sourceGames"); + dukglue_register_property(ctx, &ScInstalledObject::legacyIdentifier_get, nullptr, "legacyIdentifier"); + dukglue_register_property(ctx, &ScInstalledObject::authors_get, nullptr, "authors"); + dukglue_register_property(ctx, &ScInstalledObject::name_get, nullptr, "name"); + } + + private: + std::string path_get() const + { + auto installedObject = GetInstalledObject(); + if (installedObject != nullptr) + { + return installedObject->Path; + } + return {}; + } + + std::string generation_get() const + { + auto installedObject = GetInstalledObject(); + if (installedObject != nullptr) + { + if (installedObject->Generation == ObjectGeneration::DAT) + return "dat"; + else + return "json"; + } + return {}; + } + + std::vector sourceGames_get() const + { + std::vector result; + auto installedObject = GetInstalledObject(); + if (installedObject != nullptr) + { + for (const auto& sourceGame : installedObject->Sources) + { + result.push_back(std::string(ObjectSourceGameToString(sourceGame))); + } + } + return result; + } + + std::string type_get() const + { + auto installedObject = GetInstalledObject(); + if (installedObject != nullptr) + { + return std::string(ObjectTypeToString(EnumValue(installedObject->Type))); + } + return {}; + } + + std::string identifier_get() const + { + auto installedObject = GetInstalledObject(); + if (installedObject != nullptr) + { + return installedObject->Identifier; + } + return {}; + } + + std::string legacyIdentifier_get() const + { + auto installedObject = GetInstalledObject(); + if (installedObject != nullptr) + { + if (!installedObject->ObjectEntry.IsEmpty()) + { + return std::string(installedObject->ObjectEntry.GetName()); + } + } + return {}; + } + + std::vector authors_get() const + { + auto installedObject = GetInstalledObject(); + if (installedObject != nullptr) + { + return installedObject->Authors; + } + return {}; + } + + std::string name_get() const + { + auto installedObject = GetInstalledObject(); + if (installedObject != nullptr) + { + return installedObject->Name; + } + return {}; + } + + const ObjectRepositoryItem* GetInstalledObject() const + { + auto context = GetContext(); + auto& objectRepository = context->GetObjectRepository(); + auto numObjects = objectRepository.GetNumObjects(); + if (_index < numObjects) + { + auto* objects = objectRepository.GetObjects(); + return &objects[_index]; + } + return nullptr; + } + }; +} // namespace OpenRCT2::Scripting + +#endif diff --git a/src/openrct2/scripting/bindings/object/ScObject.hpp b/src/openrct2/scripting/bindings/object/ScObject.hpp index 589d71c138..974a955175 100644 --- a/src/openrct2/scripting/bindings/object/ScObject.hpp +++ b/src/openrct2/scripting/bindings/object/ScObject.hpp @@ -19,6 +19,7 @@ # include "../../../object/SmallSceneryObject.h" # include "../../Duktape.hpp" # include "../../ScriptEngine.h" +# include "ScInstalledObject.hpp" # include # include @@ -40,6 +41,7 @@ namespace OpenRCT2::Scripting static void Register(duk_context* ctx) { + dukglue_register_property(ctx, &ScObject::installedObject_get, nullptr, "installedObject"); dukglue_register_property(ctx, &ScObject::type_get, nullptr, "type"); dukglue_register_property(ctx, &ScObject::index_get, nullptr, "index"); dukglue_register_property(ctx, &ScObject::identifier_get, nullptr, "identifier"); @@ -62,33 +64,22 @@ namespace OpenRCT2::Scripting return std::nullopt; } - static std::string_view ObjectTypeToString(uint8_t type) + private: + std::shared_ptr installedObject_get() const { - static constexpr std::string_view Types[] = { - "ride", - "small_scenery", - "large_scenery", - "wall", - "banner", - "footpath", - "footpath_addition", - "scenery_group", - "park_entrance", - "water", - "stex", - "terrain_surface", - "terrain_edge", - "station", - "music", - "footpath_surface", - "footpath_railings", - }; - if (type >= std::size(Types)) - return "unknown"; - return Types[type]; + auto obj = GetObject(); + if (obj != nullptr) + { + auto& objectRepository = GetContext()->GetObjectRepository(); + auto installedObject = objectRepository.FindObject(obj->GetIdentifier()); + if (installedObject != nullptr) + { + return std::make_shared(installedObject->Id); + } + } + return {}; } - private: std::string type_get() const { return std::string(ObjectTypeToString(EnumValue(_type))); From 9a20c47f86ac270d1d3f7746263b15d9eeaeea22 Mon Sep 17 00:00:00 2001 From: Ted John Date: Mon, 11 Apr 2022 23:27:35 +0100 Subject: [PATCH 11/17] Design APIs for loading / unloading objects --- distribution/openrct2.d.ts | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/distribution/openrct2.d.ts b/distribution/openrct2.d.ts index cf53310bf8..8838eb9888 100644 --- a/distribution/openrct2.d.ts +++ b/distribution/openrct2.d.ts @@ -4704,4 +4704,42 @@ declare global { readonly parents: number[]; readonly children: number[]; } + + interface ObjectManager { + readonly installedObjects: InstalledObject[]; + + /** + * 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 + * will replace it, providing the object type is the same. + * @param identifier The object identifier. + * @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; + + /** + * Attempt to load the given objects into the current park, given they are not already loaded. + */ + load(identifiers: string[]): void; + + /** + * Unloads the object, if loaded. + * @param identifier The object identifier to unload. + */ + unload(identifier: string): void; + + /** + * Unloads the specified objects, if loaded. + * @param identifiers The object identifiers to unload. + */ + unload(identifiers: string[]): void; + + /** + * Unloads the specified object, if loaded. + * @param type The object type. + * @param index The index of the slot to unload for the given type. + */ + unload(type: ObjectType, index: number): void; + } } From 069a2b319286df7e7669eb6ca3a9f3f3435fee74 Mon Sep 17 00:00:00 2001 From: Ted John Date: Sat, 29 Apr 2023 17:39:35 +0100 Subject: [PATCH 12/17] Implement object manager for plugins --- distribution/openrct2.d.ts | 54 +++- src/openrct2/libopenrct2.vcxproj | 3 + src/openrct2/object/ObjectManager.cpp | 6 + src/openrct2/object/ObjectManager.h | 1 + src/openrct2/object/ObjectTypes.cpp | 10 +- src/openrct2/object/ObjectTypes.h | 1 + src/openrct2/scripting/ScriptEngine.cpp | 3 + .../scripting/bindings/game/ScContext.hpp | 70 +---- .../bindings/object/ScObjectManager.cpp | 270 ++++++++++++++++++ .../bindings/object/ScObjectManager.h | 43 +++ 10 files changed, 392 insertions(+), 69 deletions(-) create mode 100644 src/openrct2/scripting/bindings/object/ScObjectManager.cpp create mode 100644 src/openrct2/scripting/bindings/object/ScObjectManager.h 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 From cbd4c78cf6e0f39fc996a957acdfba7eeebcecba Mon Sep 17 00:00:00 2001 From: Ted John Date: Sun, 30 Apr 2023 14:42:22 +0100 Subject: [PATCH 13/17] Handle legacy objects correctly --- src/openrct2/object/Object.cpp | 23 +++++++++++++++++++ src/openrct2/object/Object.h | 2 ++ .../bindings/object/ScInstalledObject.hpp | 9 +++++++- .../scripting/bindings/object/ScObject.hpp | 2 +- .../bindings/object/ScObjectManager.cpp | 17 ++++++++++---- 5 files changed, 47 insertions(+), 6 deletions(-) diff --git a/src/openrct2/object/Object.cpp b/src/openrct2/object/Object.cpp index d2a0bacfd0..c7a9348dd6 100644 --- a/src/openrct2/object/Object.cpp +++ b/src/openrct2/object/Object.cpp @@ -98,6 +98,29 @@ std::string ObjectEntryDescriptor::ToString() const } } +static uint32_t ParseHex(std::string_view x) +{ + assert(x.size() != 8); + char buffer[9]; + std::memcpy(buffer, x.data(), 8); + buffer[8] = 0; + char* endp{}; + return static_cast(std::strtol(buffer, &endp, 16)); +} + +ObjectEntryDescriptor ObjectEntryDescriptor::Parse(std::string_view identifier) +{ + if (identifier.size() == 26 && identifier[8] == '|' && identifier[17] == '|') + { + RCTObjectEntry entry{}; + entry.flags = ParseHex(identifier.substr(0, 8)); + entry.SetName(identifier.substr(9, 8)); + entry.checksum = ParseHex(identifier.substr(18)); + return ObjectEntryDescriptor(entry); + } + return ObjectEntryDescriptor(identifier); +} + bool ObjectEntryDescriptor::operator==(const ObjectEntryDescriptor& rhs) const { if (Generation != rhs.Generation) diff --git a/src/openrct2/object/Object.h b/src/openrct2/object/Object.h index 21a715628e..36a3886904 100644 --- a/src/openrct2/object/Object.h +++ b/src/openrct2/object/Object.h @@ -143,6 +143,8 @@ struct ObjectEntryDescriptor bool operator==(const ObjectEntryDescriptor& rhs) const; bool operator!=(const ObjectEntryDescriptor& rhs) const; + + static ObjectEntryDescriptor Parse(std::string_view identifier); }; struct IObjectRepository; diff --git a/src/openrct2/scripting/bindings/object/ScInstalledObject.hpp b/src/openrct2/scripting/bindings/object/ScInstalledObject.hpp index eb8178d33b..f722280a38 100644 --- a/src/openrct2/scripting/bindings/object/ScInstalledObject.hpp +++ b/src/openrct2/scripting/bindings/object/ScInstalledObject.hpp @@ -133,7 +133,14 @@ namespace OpenRCT2::Scripting auto installedObject = GetInstalledObject(); if (installedObject != nullptr) { - return installedObject->Identifier; + if (installedObject->Generation == ObjectGeneration::DAT) + { + return ObjectEntryDescriptor(installedObject->ObjectEntry).ToString(); + } + else + { + return installedObject->Identifier; + } } return {}; } diff --git a/src/openrct2/scripting/bindings/object/ScObject.hpp b/src/openrct2/scripting/bindings/object/ScObject.hpp index 974a955175..bd5a47b86a 100644 --- a/src/openrct2/scripting/bindings/object/ScObject.hpp +++ b/src/openrct2/scripting/bindings/object/ScObject.hpp @@ -71,7 +71,7 @@ namespace OpenRCT2::Scripting if (obj != nullptr) { auto& objectRepository = GetContext()->GetObjectRepository(); - auto installedObject = objectRepository.FindObject(obj->GetIdentifier()); + auto installedObject = objectRepository.FindObject(obj->GetDescriptor()); if (installedObject != nullptr) { return std::make_shared(installedObject->Id); diff --git a/src/openrct2/scripting/bindings/object/ScObjectManager.cpp b/src/openrct2/scripting/bindings/object/ScObjectManager.cpp index 6ac67415b1..0e450e219e 100644 --- a/src/openrct2/scripting/bindings/object/ScObjectManager.cpp +++ b/src/openrct2/scripting/bindings/object/ScObjectManager.cpp @@ -1,3 +1,12 @@ +/***************************************************************************** + * Copyright (c) 2014-2023 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. + *****************************************************************************/ + #include "ScObjectManager.h" #include "../../../object/ObjectList.h" @@ -51,7 +60,7 @@ DukValue ScObjectManager::load(const DukValue& p1, const DukValue& p2) throw DukException() << "Expected string for 'identifier'."; const auto& identifier = item.as_string(); - descriptors.emplace_back(identifier); + descriptors.push_back(ObjectEntryDescriptor::Parse(identifier)); } duk_push_array(ctx); @@ -84,7 +93,7 @@ DukValue ScObjectManager::load(const DukValue& p1, const DukValue& p2) throw DukException() << "Expected string for 'identifier'."; const auto& identifier = p1.as_string(); - ObjectEntryDescriptor descriptor(identifier); + auto descriptor = ObjectEntryDescriptor::Parse(identifier); auto installedObject = objectRepository.FindObject(descriptor); if (installedObject != nullptr) @@ -154,7 +163,7 @@ void ScObjectManager::unload(const DukValue& p1, const DukValue& p2) else { // unload(identifier) - objectManager.UnloadObjects({ ObjectEntryDescriptor(szP1) }); + objectManager.UnloadObjects({ ObjectEntryDescriptor::Parse(szP1) }); } } else if (p1.is_array()) @@ -166,7 +175,7 @@ void ScObjectManager::unload(const DukValue& p1, const DukValue& p2) { if (identifier.type() == DukValue::STRING) { - descriptors.emplace_back(identifier.as_string()); + descriptors.push_back(ObjectEntryDescriptor::Parse(identifier.as_string())); } } objectManager.UnloadObjects(descriptors); From c784084942a48462df3d80242b8f6f5cb1b65906 Mon Sep 17 00:00:00 2001 From: Ted John Date: Sun, 30 Apr 2023 14:50:25 +0100 Subject: [PATCH 14/17] Update openrct2.d.ts --- distribution/openrct2.d.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/distribution/openrct2.d.ts b/distribution/openrct2.d.ts index b71ddc47c0..452723fc7b 100644 --- a/distribution/openrct2.d.ts +++ b/distribution/openrct2.d.ts @@ -4731,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): LoadedObject; + load(identifier: string, index?: number): LoadedObject | null; /** * Attempt to load the given objects into the current park, given they are not already loaded. */ - load(identifiers: string[]): LoadedObject[]; + load(identifiers: string[]): (LoadedObject | null)[]; /** * Unloads the object, if loaded. @@ -4762,7 +4762,7 @@ declare global { * @param type The object type. * @param index The index. */ - getObject(type: ObjectType, index: number): LoadedImageObject; + getObject(type: ObjectType, index: number): LoadedObject; getObject(type: "ride", index: number): RideObject; getObject(type: "small_scenery", index: number): SmallSceneryObject; getObject(type: "large_scenery", index: number): LargeSceneryObject; @@ -4776,7 +4776,7 @@ declare global { * Gets all the currently loaded objects for a given object type. * @param type The object type. */ - getAllObjects(type: ObjectType): LoadedImageObject[]; + getAllObjects(type: ObjectType): LoadedObject[]; getAllObjects(type: "ride"): RideObject[]; getAllObjects(type: "small_scenery"): SmallSceneryObject[]; getAllObjects(type: "large_scenery"): LargeSceneryObject[]; From ffe5f74fa30f35e7bf757dd2982878f0b8a94edc Mon Sep 17 00:00:00 2001 From: Ted John Date: Sun, 30 Apr 2023 16:28:41 +0100 Subject: [PATCH 15/17] Update openrct2.d.ts --- distribution/openrct2.d.ts | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/distribution/openrct2.d.ts b/distribution/openrct2.d.ts index 452723fc7b..05447e94b7 100644 --- a/distribution/openrct2.d.ts +++ b/distribution/openrct2.d.ts @@ -224,26 +224,35 @@ declare global { * @deprecated Use {@link ObjectManager.getObject} instead. */ getObject(type: ObjectType, index: number): LoadedImageObject; + + /** + * @deprecated Use {@link ObjectManager.getObject} instead. + */ getObject(type: "ride", index: number): RideObject; + + /** + * @deprecated Use {@link ObjectManager.getObject} instead. + */ 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; + + /** + * @deprecated Use {@link ObjectManager.getObject} instead. + */ getObject(type: "music", index: number): LoadedObject; /** * @deprecated Use {@link ObjectManager.getAllObjects} instead. */ getAllObjects(type: ObjectType): LoadedImageObject[]; + + /** + * @deprecated Use {@link ObjectManager.getAllObjects} instead. + */ 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[]; + + /** + * @deprecated Use {@link ObjectManager.getAllObjects} instead. + */ getAllObjects(type: "music"): LoadedObject[]; /** @@ -1640,7 +1649,7 @@ declare global { /** * The unique identifier of the object, e.g. "rct2.burgb". - * Only JSON objects will have an identifier. + * For legacy DAT objects, the identifier will be in a format similar to "09F55405|DirtGras|B9B19A7F". */ readonly identifier: string; @@ -1688,7 +1697,7 @@ declare global { /** * The unique identifier of the object, e.g. "rct2.burgb". - * Only JSON objects will have an identifier. + * For legacy DAT objects, the identifier will be in a format similar to "09F55405|DirtGras|B9B19A7F". */ readonly identifier: string; From 0ed4bfbc4a27d7fa0950a22219efb224e81f749b Mon Sep 17 00:00:00 2001 From: Ted John Date: Sun, 30 Apr 2023 17:18:09 +0100 Subject: [PATCH 16/17] Return null for legacyIdentifier --- distribution/openrct2.d.ts | 2 +- .../scripting/bindings/object/ScInstalledObject.hpp | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/distribution/openrct2.d.ts b/distribution/openrct2.d.ts index 05447e94b7..bd1f2caf39 100644 --- a/distribution/openrct2.d.ts +++ b/distribution/openrct2.d.ts @@ -1658,7 +1658,7 @@ declare global { * This may have trailing spaces if the name is shorter than 8 characters. * Only .DAT objects or JSON objects based on .DAT objects will have legacy identifiers. */ - readonly legacyIdentifier: string; + readonly legacyIdentifier: string | null; /** * The object version, e.g. "1.5.2-pre". diff --git a/src/openrct2/scripting/bindings/object/ScInstalledObject.hpp b/src/openrct2/scripting/bindings/object/ScInstalledObject.hpp index f722280a38..39b652fade 100644 --- a/src/openrct2/scripting/bindings/object/ScInstalledObject.hpp +++ b/src/openrct2/scripting/bindings/object/ScInstalledObject.hpp @@ -145,17 +145,18 @@ namespace OpenRCT2::Scripting return {}; } - std::string legacyIdentifier_get() const + DukValue legacyIdentifier_get() const { + auto ctx = GetContext()->GetScriptEngine().GetContext(); auto installedObject = GetInstalledObject(); if (installedObject != nullptr) { if (!installedObject->ObjectEntry.IsEmpty()) { - return std::string(installedObject->ObjectEntry.GetName()); + return ToDuk(ctx, installedObject->ObjectEntry.GetName()); } } - return {}; + return ToDuk(ctx, nullptr); } std::vector authors_get() const From 79f8913065f12dadca6ce3b606a2c357a48a018b Mon Sep 17 00:00:00 2001 From: Ted John Date: Sun, 30 Apr 2023 17:20:28 +0100 Subject: [PATCH 17/17] Fix CI due to sign issue --- src/openrct2/object/ObjectTypes.h | 1 + .../bindings/object/ScObjectManager.cpp | 18 +++++++++++------- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/openrct2/object/ObjectTypes.h b/src/openrct2/object/ObjectTypes.h index 12ab8a4dbc..ea8f32ea65 100644 --- a/src/openrct2/object/ObjectTypes.h +++ b/src/openrct2/object/ObjectTypes.h @@ -10,6 +10,7 @@ #pragma once #include +#include #include #include diff --git a/src/openrct2/scripting/bindings/object/ScObjectManager.cpp b/src/openrct2/scripting/bindings/object/ScObjectManager.cpp index 0e450e219e..3078f5e117 100644 --- a/src/openrct2/scripting/bindings/object/ScObjectManager.cpp +++ b/src/openrct2/scripting/bindings/object/ScObjectManager.cpp @@ -7,12 +7,14 @@ * OpenRCT2 is licensed under the GNU General Public License version 3. *****************************************************************************/ -#include "ScObjectManager.h" +#ifdef ENABLE_SCRIPTING -#include "../../../object/ObjectList.h" -#include "../../../ride/RideData.h" -#include "../../Duktape.hpp" -#include "../../ScriptEngine.h" +# include "ScObjectManager.h" + +# include "../../../object/ObjectList.h" +# include "../../../ride/RideData.h" +# include "../../Duktape.hpp" +# include "../../ScriptEngine.h" using namespace OpenRCT2; using namespace OpenRCT2::Scripting; @@ -103,7 +105,7 @@ DukValue ScObjectManager::load(const DukValue& p1, const DukValue& p2) if (p2.type() != DukValue::NUMBER) throw DukException() << "Expected number for 'index'."; - auto index = p2.as_int(); + auto index = static_cast(p2.as_int()); auto limit = GetObjectTypeLimit(installedObject->Type); if (index < limit) { @@ -112,7 +114,7 @@ DukValue ScObjectManager::load(const DukValue& p1, const DukValue& p2) { objectManager.UnloadObjects({ loadedObject->GetDescriptor() }); } - auto obj = objectManager.LoadObject(descriptor, index); + auto obj = objectManager.LoadObject(descriptor, static_cast(index)); if (obj != nullptr) { MarkAsResearched(obj); @@ -277,3 +279,5 @@ DukValue ScObjectManager::CreateScObject(duk_context* ctx, ObjectType type, int3 return GetObjectAsDukValue(ctx, std::make_shared(type, index)); } } + +#endif