From 831c7651bbdcaafd96e092e1c13956597386efab Mon Sep 17 00:00:00 2001 From: Aaron van Geffen Date: Sun, 5 May 2024 22:41:52 +0200 Subject: [PATCH] Scripting: tailor costume assignment to each staff type In order to transition staff costumes to objects, we must further disentangle staff from regular peeps. This has many advantages, such as making custom entertainers or even handymen costumes. However, this means putting some restrictions on what costumes can be assigned to staff in the mean while. We are aware of plug-ins allowing staff to be decorated like normal peeps, though, e.g. using @Manticore-007's Peep Editor. Splitting staff from peeps will mean breaking such functionality. We can do our very best to reverting 'invalid' staff to their normal outfits instead of them outright disappearing. However, in the mean time, we should disallow peep costumes from being assigned to staff to prevent further disappointment down the line. Once we get to actually adding custom staff costumes, I plan to add a new plug-in API to get available costumes for a particular staff type. This would apply to entertainers, but also other staff types. This should make it easier for plug-in authors to tap into custom costumes in the future. --- distribution/changelog.txt | 1 + distribution/openrct2.d.ts | 29 ++++- src/openrct2/core/EnumMap.hpp | 2 +- src/openrct2/entity/Staff.cpp | 12 -- src/openrct2/entity/Staff.h | 2 - src/openrct2/scripting/ScriptEngine.h | 2 +- .../scripting/bindings/entity/ScStaff.cpp | 110 ++++++++++++++++-- .../scripting/bindings/entity/ScStaff.hpp | 5 +- 8 files changed, 134 insertions(+), 29 deletions(-) diff --git a/distribution/changelog.txt b/distribution/changelog.txt index b1b898209d..a6fac3a6d7 100644 --- a/distribution/changelog.txt +++ b/distribution/changelog.txt @@ -1,5 +1,6 @@ 0.4.12 (in development) ------------------------------------------------------------------------ +- Feature: [#21714] [Plugin] Costume assignment is now tailored to each staff type. 0.4.11 (2024-05-05) ------------------------------------------------------------------------ diff --git a/distribution/openrct2.d.ts b/distribution/openrct2.d.ts index 4cc2078b17..473648a89c 100644 --- a/distribution/openrct2.d.ts +++ b/distribution/openrct2.d.ts @@ -3095,6 +3095,24 @@ declare global { readonly item: GuestItemType; } + type StaffCostume = + "none" | + "handyman" | + "mechanic" | + "security1" | + "security2" | + "panda" | + "tiger" | + "elephant" | + "roman" | + "gorilla" | + "snowman" | + "knight" | + "astronaut" | + "bandit" | + "sheriff" | + "pirate"; + /** * Represents a staff member. */ @@ -3105,14 +3123,19 @@ declare global { staffType: StaffType; /** - * Colour of the staff member. Not applicable for entertainers. + * Colour of the staff member. Not applicable to entertainers. */ colour: number; /** - * The entertainer's costume, only applicable for entertainers. + * Array of costumes available to this particular staff member. */ - costume: number; + readonly availableCostumes: StaffCostume[]; + + /** + * The staff member's costume. + */ + costume: StaffCostume | string | number; /** * The enabled jobs the staff can do, e.g. sweep litter, water plants, inspect rides etc. diff --git a/src/openrct2/core/EnumMap.hpp b/src/openrct2/core/EnumMap.hpp index bcf96ffb2b..f34562c163 100644 --- a/src/openrct2/core/EnumMap.hpp +++ b/src/openrct2/core/EnumMap.hpp @@ -62,7 +62,7 @@ public: { std::sort(_map.begin(), _map.end(), [](const auto& a, const auto& b) { return a.second < b.second; }); - if constexpr (ValueIndexable()) + if (ValueIndexable() && _map.size() > 1) { _continiousValueIndex = true; T cur{}; diff --git a/src/openrct2/entity/Staff.cpp b/src/openrct2/entity/Staff.cpp index c1e58ba9f7..1928121cd8 100644 --- a/src/openrct2/entity/Staff.cpp +++ b/src/openrct2/entity/Staff.cpp @@ -947,18 +947,6 @@ bool Staff::DoPathFinding() } } -uint8_t Staff::GetCostume() const -{ - return EnumValue(SpriteType) - EnumValue(PeepSpriteType::EntertainerPanda); -} - -void Staff::SetCostume(uint8_t value) -{ - auto costume = static_cast(value); - SpriteType = EntertainerCostumeToSprite(costume); - UpdateAction(); -} - void Staff::SetHireDate(int32_t hireDate) { HireDate = hireDate; diff --git a/src/openrct2/entity/Staff.h b/src/openrct2/entity/Staff.h index e8cc7b4c62..871dafc993 100644 --- a/src/openrct2/entity/Staff.h +++ b/src/openrct2/entity/Staff.h @@ -51,8 +51,6 @@ public: bool IsLocationInPatrol(const CoordsXY& loc) const; bool IsLocationOnPatrolEdge(const CoordsXY& loc) const; bool DoPathFinding(); - uint8_t GetCostume() const; - void SetCostume(uint8_t value); void SetHireDate(int32_t hireDate); int32_t GetHireDate() const; diff --git a/src/openrct2/scripting/ScriptEngine.h b/src/openrct2/scripting/ScriptEngine.h index e0e64948bd..381044d17d 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 = 85; + static constexpr int32_t OPENRCT2_PLUGIN_API_VERSION = 86; // 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 1796decbac..dbc31aa15f 100644 --- a/src/openrct2/scripting/bindings/entity/ScStaff.cpp +++ b/src/openrct2/scripting/bindings/entity/ScStaff.cpp @@ -26,6 +26,7 @@ namespace OpenRCT2::Scripting dukglue_set_base_class(ctx); dukglue_register_property(ctx, &ScStaff::staffType_get, &ScStaff::staffType_set, "staffType"); dukglue_register_property(ctx, &ScStaff::colour_get, &ScStaff::colour_set, "colour"); + dukglue_register_property(ctx, &ScStaff::availableCostumes_get, nullptr, "availableCostumes"); dukglue_register_property(ctx, &ScStaff::costume_get, &ScStaff::costume_set, "costume"); dukglue_register_property(ctx, &ScStaff::patrolArea_get, nullptr, "patrolArea"); dukglue_register_property(ctx, &ScStaff::orders_get, &ScStaff::orders_set, "orders"); @@ -108,24 +109,117 @@ namespace OpenRCT2::Scripting } } - uint8_t ScStaff::costume_get() const + static const DukEnumMap availableHandymanCostumes({ + { "handyman", PeepSpriteType::Handyman }, + }); + + static const DukEnumMap availableMechanicCostumes({ + { "mechanic", PeepSpriteType::Mechanic }, + }); + + static const DukEnumMap availableSecurityCostumes({ + { "security1", PeepSpriteType::Security }, + { "security2", PeepSpriteType::SecurityAlt }, + }); + + static const DukEnumMap availableEntertainerCostumes({ + { "none", PeepSpriteType::Normal }, + { "panda", PeepSpriteType::EntertainerPanda }, + { "tiger", PeepSpriteType::EntertainerTiger }, + { "elephant", PeepSpriteType::EntertainerElephant }, + { "roman", PeepSpriteType::EntertainerRoman }, + { "gorilla", PeepSpriteType::EntertainerGorilla }, + { "snowman", PeepSpriteType::EntertainerSnowman }, + { "knight", PeepSpriteType::EntertainerKnight }, + { "astronaut", PeepSpriteType::EntertainerAstronaut }, + { "bandit", PeepSpriteType::EntertainerBandit }, + { "sheriff", PeepSpriteType::EntertainerSheriff }, + { "pirate", PeepSpriteType::EntertainerPirate }, + }); + + static const DukEnumMap& costumesByStaffType(StaffType staffType) { - auto peep = GetStaff(); - if (peep != nullptr && peep->AssignedStaffType == StaffType::Entertainer) + switch (staffType) { - return peep->GetCostume(); + case StaffType::Handyman: + return availableHandymanCostumes; + case StaffType::Mechanic: + return availableMechanicCostumes; + case StaffType::Security: + return availableSecurityCostumes; + case StaffType::Entertainer: + default: + return availableEntertainerCostumes; } - return 0; } - void ScStaff::costume_set(uint8_t value) + std::vector ScStaff::availableCostumes_get() const { - ThrowIfGameStateNotMutable(); + std::vector availableCostumes{}; auto peep = GetStaff(); if (peep != nullptr) { - peep->SetCostume(value); + for (auto& costume : costumesByStaffType(peep->AssignedStaffType)) + { + availableCostumes.push_back(std::string(costume.first)); + } } + return availableCostumes; + } + + std::string ScStaff::costume_get() const + { + auto peep = GetStaff(); + if (peep == nullptr) + { + return nullptr; + } + + auto& availableCostumes = costumesByStaffType(peep->AssignedStaffType); + + auto costume = availableCostumes.find(peep->SpriteType); + if (costume != availableCostumes.end()) + { + return std::string(costume->first); + } + else + return nullptr; + } + + void ScStaff::costume_set(const DukValue& value) + { + ThrowIfGameStateNotMutable(); + + auto peep = GetStaff(); + if (peep == nullptr) + { + return; + } + + auto& availableCostumes = costumesByStaffType(peep->AssignedStaffType); + + // Split by type passed so as to not break old plugins + if (value.type() == DukValue::Type::STRING) + { + std::string newCostume = value.as_string(); + auto newSpriteType = availableCostumes.TryGet(newCostume); + if (newSpriteType != std::nullopt) + { + peep->SpriteType = *newSpriteType; + return; + } + } + else if (value.type() == DukValue::Type::NUMBER) + { + auto newSpriteType = PeepSpriteType(value.as_uint() + EnumValue(PeepSpriteType::EntertainerPanda)); + if (availableCostumes.find(newSpriteType) != availableCostumes.end()) + { + peep->SpriteType = newSpriteType; + return; + } + } + + throw DukException() << "Invalid costume for this staff member"; } std::shared_ptr ScStaff::patrolArea_get() const diff --git a/src/openrct2/scripting/bindings/entity/ScStaff.hpp b/src/openrct2/scripting/bindings/entity/ScStaff.hpp index 0cd72b7473..a6f79c3523 100644 --- a/src/openrct2/scripting/bindings/entity/ScStaff.hpp +++ b/src/openrct2/scripting/bindings/entity/ScStaff.hpp @@ -56,8 +56,9 @@ namespace OpenRCT2::Scripting uint8_t colour_get() const; void colour_set(uint8_t value); - uint8_t costume_get() const; - void costume_set(uint8_t value); + std::vector availableCostumes_get() const; + std::string costume_get() const; + void costume_set(const DukValue& value); std::shared_ptr patrolArea_get() const;