From a7496985665ca82623709376587b0fc030aae781 Mon Sep 17 00:00:00 2001 From: Arnold Zhou Date: Thu, 25 Jul 2024 09:10:34 +1000 Subject: [PATCH] [Plugin API] Feat: Expose staff statistics (#22184) --- distribution/changelog.txt | 1 + distribution/openrct2.d.ts | 64 ++++++- src/openrct2/scripting/ScriptEngine.cpp | 3 + src/openrct2/scripting/ScriptEngine.h | 2 +- .../scripting/bindings/entity/ScStaff.cpp | 164 ++++++++++++++++++ .../scripting/bindings/entity/ScStaff.hpp | 47 +++++ .../scripting/bindings/world/ScMap.cpp | 70 +++++++- 7 files changed, 346 insertions(+), 5 deletions(-) diff --git a/distribution/changelog.txt b/distribution/changelog.txt index 51642d4d70..486d852a53 100644 --- a/distribution/changelog.txt +++ b/distribution/changelog.txt @@ -3,6 +3,7 @@ - Feature: [#20831] The ride selection window now shows object authors if debugging tools are active. - Feature: [#20832] The ride music tab now shows a track listing for the current music style. - Feature: [#22172] [Plugin] Expose ride satisfaction ratings to the plugin API. +- Feature: [#22184] [Plugin] Expose staff statistics to the plugin API. - Feature: [#22213] [Plugin] Allow plugins to focus on textboxes in custom windows. - Feature: [#22301] Loading save games or scenarios now indicates loading progress. - Feature: [OpenMusic#54] Added Progressive ride music style (feat. Approaching Nirvana). diff --git a/distribution/openrct2.d.ts b/distribution/openrct2.d.ts index 012b6b1f69..d32549bc14 100644 --- a/distribution/openrct2.d.ts +++ b/distribution/openrct2.d.ts @@ -3285,7 +3285,7 @@ declare global { /** * Represents a staff member. */ - interface Staff extends Peep { + interface BaseStaff extends Peep { /** * The type of staff member, e.g. handyman, mechanic. */ @@ -3344,6 +3344,68 @@ declare global { type StaffType = "handyman" | "mechanic" | "security" | "entertainer"; + type Staff = Handyman | Mechanic | Security | Entertainer; + + /** + * Represents a handyman. + */ + interface Handyman extends BaseStaff { + staffType: "handyman"; + + /** + * The number of lawns mown by the handyman. + */ + readonly lawnsMown: number; + + /** + * The number of gardens watered by the handyman. + */ + readonly gardensWatered: number; + + /** + * The number of litter swept by the handyman. + */ + readonly litterSwept: number; + + /** + * The number of bins emptied by the handyman. + */ + readonly binsEmptied: number; + } + + /** + * Represents a mechanic. + */ + interface Mechanic extends BaseStaff { + staffType: "mechanic"; + + /** + * The number of rides fixed by the mechanic. + */ + readonly ridesFixed: number; + + /** + * The number of inspections performed by the mechanic. + */ + readonly ridesInspected: number; + } + + /** + * Represents a security guard. + */ + interface Security extends BaseStaff { + staffType: "security"; + + /** + * The number of vandals stopped by the security guard. + */ + readonly vandalsStopped: number; + } + + interface Entertainer extends BaseStaff { + staffType: "entertainer"; + } + interface PatrolArea { /** * Gets or sets the map coodinates for all individual tiles in the staff member's patrol area. diff --git a/src/openrct2/scripting/ScriptEngine.cpp b/src/openrct2/scripting/ScriptEngine.cpp index 694a0c945c..ab1beaa3a3 100644 --- a/src/openrct2/scripting/ScriptEngine.cpp +++ b/src/openrct2/scripting/ScriptEngine.cpp @@ -450,6 +450,9 @@ void ScriptEngine::Initialise() ScScenarioObjective::Register(ctx); ScPatrolArea::Register(ctx); ScStaff::Register(ctx); + ScHandyman::Register(ctx); + ScMechanic::Register(ctx); + ScSecurity::Register(ctx); ScPlugin::Register(ctx); dukglue_register_global(ctx, std::make_shared(), "cheats"); diff --git a/src/openrct2/scripting/ScriptEngine.h b/src/openrct2/scripting/ScriptEngine.h index 139a90d040..68e083b574 100644 --- a/src/openrct2/scripting/ScriptEngine.h +++ b/src/openrct2/scripting/ScriptEngine.h @@ -46,7 +46,7 @@ namespace OpenRCT2 namespace OpenRCT2::Scripting { - static constexpr int32_t OPENRCT2_PLUGIN_API_VERSION = 95; + static constexpr int32_t OPENRCT2_PLUGIN_API_VERSION = 96; // Versions marking breaking changes. static constexpr int32_t API_VERSION_33_PEEP_DEPRECATION = 33; diff --git a/src/openrct2/scripting/bindings/entity/ScStaff.cpp b/src/openrct2/scripting/bindings/entity/ScStaff.cpp index 6d6efa4dd0..1224b65750 100644 --- a/src/openrct2/scripting/bindings/entity/ScStaff.cpp +++ b/src/openrct2/scripting/bindings/entity/ScStaff.cpp @@ -438,6 +438,170 @@ namespace OpenRCT2::Scripting return static_cast(animationGroup.frame_offsets.size()); } + ScHandyman::ScHandyman(EntityId Id) + : ScStaff(Id) + { + } + + void ScHandyman::Register(duk_context* ctx) + { + dukglue_set_base_class(ctx); + dukglue_register_property(ctx, &ScHandyman::lawnsMown_get, nullptr, "lawnsMown"); + dukglue_register_property(ctx, &ScHandyman::gardensWatered_get, nullptr, "gardensWatered"); + dukglue_register_property(ctx, &ScHandyman::litterSwept_get, nullptr, "litterSwept"); + dukglue_register_property(ctx, &ScHandyman::binsEmptied_get, nullptr, "binsEmptied"); + } + + Staff* ScHandyman::GetHandyman() const + { + return ::GetEntity(_id); + } + + DukValue ScHandyman::lawnsMown_get() const + { + auto& scriptEngine = GetContext()->GetScriptEngine(); + auto* ctx = scriptEngine.GetContext(); + auto peep = GetHandyman(); + if (peep != nullptr && peep->AssignedStaffType == StaffType::Handyman) + { + duk_push_uint(ctx, peep->StaffLawnsMown); + } + else + { + duk_push_null(ctx); + } + return DukValue::take_from_stack(ctx); + } + + DukValue ScHandyman::gardensWatered_get() const + { + auto& scriptEngine = GetContext()->GetScriptEngine(); + auto* ctx = scriptEngine.GetContext(); + auto peep = GetHandyman(); + if (peep != nullptr && peep->AssignedStaffType == StaffType::Handyman) + { + duk_push_uint(ctx, peep->StaffGardensWatered); + } + else + { + duk_push_null(ctx); + } + return DukValue::take_from_stack(ctx); + } + + DukValue ScHandyman::litterSwept_get() const + { + auto& scriptEngine = GetContext()->GetScriptEngine(); + auto* ctx = scriptEngine.GetContext(); + auto peep = GetHandyman(); + if (peep != nullptr && peep->AssignedStaffType == StaffType::Handyman) + { + duk_push_uint(ctx, peep->StaffLitterSwept); + } + else + { + duk_push_null(ctx); + } + return DukValue::take_from_stack(ctx); + } + + DukValue ScHandyman::binsEmptied_get() const + { + auto& scriptEngine = GetContext()->GetScriptEngine(); + auto* ctx = scriptEngine.GetContext(); + auto peep = GetHandyman(); + if (peep != nullptr && peep->AssignedStaffType == StaffType::Handyman) + { + duk_push_uint(ctx, peep->StaffBinsEmptied); + } + else + { + duk_push_null(ctx); + } + return DukValue::take_from_stack(ctx); + } + + ScMechanic::ScMechanic(EntityId Id) + : ScStaff(Id) + { + } + + void ScMechanic::Register(duk_context* ctx) + { + dukglue_set_base_class(ctx); + dukglue_register_property(ctx, &ScMechanic::ridesFixed_get, nullptr, "ridesFixed"); + dukglue_register_property(ctx, &ScMechanic::ridesInspected_get, nullptr, "ridesInspected"); + } + + Staff* ScMechanic::GetMechanic() const + { + return ::GetEntity(_id); + } + + DukValue ScMechanic::ridesFixed_get() const + { + auto& scriptEngine = GetContext()->GetScriptEngine(); + auto* ctx = scriptEngine.GetContext(); + auto peep = GetMechanic(); + if (peep != nullptr && peep->AssignedStaffType == StaffType::Mechanic) + { + duk_push_uint(ctx, peep->StaffRidesFixed); + } + else + { + duk_push_null(ctx); + } + return DukValue::take_from_stack(ctx); + } + + DukValue ScMechanic::ridesInspected_get() const + { + auto& scriptEngine = GetContext()->GetScriptEngine(); + auto* ctx = scriptEngine.GetContext(); + auto peep = GetMechanic(); + if (peep != nullptr && peep->AssignedStaffType == StaffType::Mechanic) + { + duk_push_uint(ctx, peep->StaffRidesInspected); + } + else + { + duk_push_null(ctx); + } + return DukValue::take_from_stack(ctx); + } + + ScSecurity::ScSecurity(EntityId Id) + : ScStaff(Id) + { + } + + void ScSecurity::Register(duk_context* ctx) + { + dukglue_set_base_class(ctx); + dukglue_register_property(ctx, &ScSecurity::vandalsStopped_get, nullptr, "vandalsStopped"); + } + + Staff* ScSecurity::GetSecurity() const + { + return ::GetEntity(_id); + } + + DukValue ScSecurity::vandalsStopped_get() const + { + auto& scriptEngine = GetContext()->GetScriptEngine(); + auto* ctx = scriptEngine.GetContext(); + auto peep = GetSecurity(); + if (peep != nullptr && peep->AssignedStaffType == StaffType::Security) + { + duk_push_uint(ctx, peep->StaffVandalsStopped); + } + else + { + duk_push_null(ctx); + } + return DukValue::take_from_stack(ctx); + } + ScPatrolArea::ScPatrolArea(EntityId id) : _staffId(id) { diff --git a/src/openrct2/scripting/bindings/entity/ScStaff.hpp b/src/openrct2/scripting/bindings/entity/ScStaff.hpp index 3e4a94aa41..9b06565f7f 100644 --- a/src/openrct2/scripting/bindings/entity/ScStaff.hpp +++ b/src/openrct2/scripting/bindings/entity/ScStaff.hpp @@ -78,6 +78,53 @@ namespace OpenRCT2::Scripting uint8_t animationLength_get() const; }; + class ScHandyman : public ScStaff + { + public: + ScHandyman(EntityId Id); + + static void Register(duk_context* ctx); + + private: + Staff* GetHandyman() const; + + DukValue lawnsMown_get() const; + + DukValue gardensWatered_get() const; + + DukValue litterSwept_get() const; + + DukValue binsEmptied_get() const; + }; + + class ScMechanic : public ScStaff + { + public: + ScMechanic(EntityId Id); + + static void Register(duk_context* ctx); + + private: + Staff* GetMechanic() const; + + DukValue ridesFixed_get() const; + + DukValue ridesInspected_get() const; + }; + + class ScSecurity : public ScStaff + { + public: + ScSecurity(EntityId Id); + + static void Register(duk_context* ctx); + + private: + Staff* GetSecurity() const; + + DukValue vandalsStopped_get() const; + }; + } // namespace OpenRCT2::Scripting #endif diff --git a/src/openrct2/scripting/bindings/world/ScMap.cpp b/src/openrct2/scripting/bindings/world/ScMap.cpp index ba3dd30bdb..de59a8c083 100644 --- a/src/openrct2/scripting/bindings/world/ScMap.cpp +++ b/src/openrct2/scripting/bindings/world/ScMap.cpp @@ -158,7 +158,29 @@ namespace OpenRCT2::Scripting { for (auto sprite : EntityList()) { - result.push_back(GetObjectAsDukValue(_context, std::make_shared(sprite->Id))); + auto staff = GetEntity(sprite->Id); + if (staff != nullptr) + { + switch (staff->AssignedStaffType) + { + case StaffType::Handyman: + result.push_back(GetObjectAsDukValue(_context, std::make_shared(sprite->Id))); + break; + case StaffType::Mechanic: + result.push_back(GetObjectAsDukValue(_context, std::make_shared(sprite->Id))); + break; + case StaffType::Security: + result.push_back(GetObjectAsDukValue(_context, std::make_shared(sprite->Id))); + break; + default: + result.push_back(GetObjectAsDukValue(_context, std::make_shared(sprite->Id))); + break; + } + } + else + { + result.push_back(GetObjectAsDukValue(_context, std::make_shared(sprite->Id))); + } } } else if (type == "crashed_vehicle_particle") @@ -225,7 +247,29 @@ namespace OpenRCT2::Scripting { for (auto sprite : EntityTileList(pos)) { - result.push_back(GetObjectAsDukValue(_context, std::make_shared(sprite->Id))); + auto staff = GetEntity(sprite->Id); + if (staff != nullptr) + { + switch (staff->AssignedStaffType) + { + case StaffType::Handyman: + result.push_back(GetObjectAsDukValue(_context, std::make_shared(sprite->Id))); + break; + case StaffType::Mechanic: + result.push_back(GetObjectAsDukValue(_context, std::make_shared(sprite->Id))); + break; + case StaffType::Security: + result.push_back(GetObjectAsDukValue(_context, std::make_shared(sprite->Id))); + break; + default: + result.push_back(GetObjectAsDukValue(_context, std::make_shared(sprite->Id))); + break; + } + } + else + { + result.push_back(GetObjectAsDukValue(_context, std::make_shared(sprite->Id))); + } } } else if (type == "crashed_vehicle_particle") @@ -357,7 +401,27 @@ namespace OpenRCT2::Scripting case EntityType::Vehicle: return GetObjectAsDukValue(_context, std::make_shared(spriteId)); case EntityType::Staff: - return GetObjectAsDukValue(_context, std::make_shared(spriteId)); + { + auto staff = GetEntity(spriteId); + if (staff != nullptr) + { + switch (staff->AssignedStaffType) + { + case StaffType::Handyman: + return GetObjectAsDukValue(_context, std::make_shared(spriteId)); + case StaffType::Mechanic: + return GetObjectAsDukValue(_context, std::make_shared(spriteId)); + case StaffType::Security: + return GetObjectAsDukValue(_context, std::make_shared(spriteId)); + default: + return GetObjectAsDukValue(_context, std::make_shared(spriteId)); + } + } + else + { + return GetObjectAsDukValue(_context, std::make_shared(spriteId)); + } + } case EntityType::Guest: return GetObjectAsDukValue(_context, std::make_shared(spriteId)); case EntityType::Litter: