diff --git a/src/openrct2/peep/PeepAnimations.cpp b/src/openrct2/peep/PeepAnimations.cpp index e3ef5d45c8..7cf9bb78b1 100644 --- a/src/openrct2/peep/PeepAnimations.cpp +++ b/src/openrct2/peep/PeepAnimations.cpp @@ -170,6 +170,36 @@ namespace OpenRCT2 return !out.empty() ? out[0] : OBJECT_ENTRY_INDEX_NULL; } + std::vector getAnimationGroupsByPeepType(const AnimationPeepType type) + { + std::vector groups{}; + + auto& objManager = GetContext()->GetObjectManager(); + for (auto i = 0u; i < kMaxPeepAnimationsObjects; i++) + { + auto* animObj = objManager.GetLoadedObject(i); + if (animObj == nullptr || animObj->GetPeepType() != type) + continue; + + for (auto j = 0u; j < animObj->GetNumAnimationGroups(); j++) + { + auto group = PeepAnimationGroup(j); + auto scriptName = animObj->GetScriptName(group); + if (scriptName.empty()) + continue; + + groups.push_back({ + .objectId = ObjectEntryIndex(i), + .group = group, + .legacyPosition = animObj->GetLegacyPosition(group), + .scriptName = scriptName, + }); + } + } + + return groups; + } + // Adapted from CarEntry.cpp SpriteBounds inferMaxAnimationDimensions(const PeepAnimation& anim) { diff --git a/src/openrct2/peep/PeepAnimations.h b/src/openrct2/peep/PeepAnimations.h index e3a7b87172..6db0209c34 100644 --- a/src/openrct2/peep/PeepAnimations.h +++ b/src/openrct2/peep/PeepAnimations.h @@ -75,5 +75,15 @@ namespace OpenRCT2 ObjectEntryIndex findRandomPeepAnimationsIndexForType(const AnimationPeepType type); + struct AnimationGroupResult + { + ObjectEntryIndex objectId; + PeepAnimationGroup group; + PeepAnimationGroup legacyPosition; + std::string_view scriptName; + }; + + std::vector getAnimationGroupsByPeepType(const AnimationPeepType type); + SpriteBounds inferMaxAnimationDimensions(const PeepAnimation& anim); } // namespace OpenRCT2 diff --git a/src/openrct2/scripting/ScriptEngine.h b/src/openrct2/scripting/ScriptEngine.h index a84841220d..c549983024 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 = 103; + static constexpr int32_t OPENRCT2_PLUGIN_API_VERSION = 104; // 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 d907a0366a..526f1520f3 100644 --- a/src/openrct2/scripting/bindings/entity/ScStaff.cpp +++ b/src/openrct2/scripting/bindings/entity/ScStaff.cpp @@ -76,22 +76,26 @@ namespace OpenRCT2::Scripting if (value == "handyman" && peep->AssignedStaffType != StaffType::Handyman) { peep->AssignedStaffType = StaffType::Handyman; - peep->AnimationGroup = PeepAnimationGroup::Handyman; + peep->AnimationObjectIndex = findPeepAnimationsIndexForType(AnimationPeepType::Handyman); + peep->AnimationGroup = PeepAnimationGroup::Normal; } else if (value == "mechanic" && peep->AssignedStaffType != StaffType::Mechanic) { peep->AssignedStaffType = StaffType::Mechanic; - peep->AnimationGroup = PeepAnimationGroup::Mechanic; + peep->AnimationObjectIndex = findPeepAnimationsIndexForType(AnimationPeepType::Mechanic); + peep->AnimationGroup = PeepAnimationGroup::Normal; } else if (value == "security" && peep->AssignedStaffType != StaffType::Security) { peep->AssignedStaffType = StaffType::Security; - peep->AnimationGroup = PeepAnimationGroup::Security; + peep->AnimationObjectIndex = findPeepAnimationsIndexForType(AnimationPeepType::Security); + peep->AnimationGroup = PeepAnimationGroup::Normal; } else if (value == "entertainer" && peep->AssignedStaffType != StaffType::Entertainer) { peep->AssignedStaffType = StaffType::Entertainer; - peep->AnimationGroup = PeepAnimationGroup::EntertainerPanda; + peep->AnimationObjectIndex = findPeepAnimationsIndexForType(AnimationPeepType::Entertainer); + peep->AnimationGroup = PeepAnimationGroup::Normal; } // Reset state to walking to prevent invalid actions from carrying over @@ -117,47 +121,20 @@ namespace OpenRCT2::Scripting } } - static const DukEnumMap availableHandymanCostumes({ - { "handyman", PeepAnimationGroup::Handyman }, - }); - - static const DukEnumMap availableMechanicCostumes({ - { "mechanic", PeepAnimationGroup::Mechanic }, - }); - - static const DukEnumMap availableSecurityCostumes({ - { "security1", PeepAnimationGroup::Security }, - { "security2", PeepAnimationGroup::SecurityAlt }, - }); - - static const DukEnumMap availableEntertainerCostumes({ - { "none", PeepAnimationGroup::Normal }, - { "panda", PeepAnimationGroup::EntertainerPanda }, - { "tiger", PeepAnimationGroup::EntertainerTiger }, - { "elephant", PeepAnimationGroup::EntertainerElephant }, - { "roman", PeepAnimationGroup::EntertainerRoman }, - { "gorilla", PeepAnimationGroup::EntertainerGorilla }, - { "snowman", PeepAnimationGroup::EntertainerSnowman }, - { "knight", PeepAnimationGroup::EntertainerKnight }, - { "astronaut", PeepAnimationGroup::EntertainerAstronaut }, - { "bandit", PeepAnimationGroup::EntertainerBandit }, - { "sheriff", PeepAnimationGroup::EntertainerSheriff }, - { "pirate", PeepAnimationGroup::EntertainerPirate }, - }); - - static const DukEnumMap& costumesByStaffType(StaffType staffType) + static const std::vector costumesByStaffType(StaffType staffType) { + // TODO: shouldn't get hit repeatedly, but cache these if (and only if) it's too slow switch (staffType) { case StaffType::Handyman: - return availableHandymanCostumes; + return getAnimationGroupsByPeepType(AnimationPeepType::Handyman); case StaffType::Mechanic: - return availableMechanicCostumes; + return getAnimationGroupsByPeepType(AnimationPeepType::Mechanic); case StaffType::Security: - return availableSecurityCostumes; + return getAnimationGroupsByPeepType(AnimationPeepType::Security); case StaffType::Entertainer: default: - return availableEntertainerCostumes; + return getAnimationGroupsByPeepType(AnimationPeepType::Entertainer); } } @@ -169,7 +146,7 @@ namespace OpenRCT2::Scripting { for (auto& costume : costumesByStaffType(peep->AssignedStaffType)) { - availableCostumes.push_back(std::string(costume.first)); + availableCostumes.push_back(std::string(costume.scriptName)); } } return availableCostumes; @@ -180,18 +157,22 @@ namespace OpenRCT2::Scripting auto peep = GetStaff(); if (peep == nullptr) { - return nullptr; + return ""; } - auto& availableCostumes = costumesByStaffType(peep->AssignedStaffType); + auto& costumes = costumesByStaffType(peep->AssignedStaffType); - auto costume = availableCostumes.find(peep->AnimationGroup); - if (costume != availableCostumes.end()) + // Find the peep's current costume in the list + auto costume = std::find_if(costumes.begin(), costumes.end(), [peep](auto& candidate) { + return candidate.objectId == peep->AnimationObjectIndex; + }); + + if (costume != costumes.end()) { - return std::string(costume->first); + return std::string(costume->scriptName); } else - return nullptr; + return ""; } void ScStaff::costume_set(const DukValue& value) @@ -204,30 +185,30 @@ namespace OpenRCT2::Scripting return; } - auto& availableCostumes = costumesByStaffType(peep->AssignedStaffType); + auto& costumes = costumesByStaffType(peep->AssignedStaffType); + auto costume = costumes.end(); // Split by type passed so as to not break old plugins if (value.type() == DukValue::Type::STRING) { - std::string newCostume = value.as_string(); - auto newAnimationGroup = availableCostumes.TryGet(newCostume); - if (newAnimationGroup != std::nullopt) - { - peep->AnimationGroup = *newAnimationGroup; - return; - } + // Find the peep's current costume by string + costume = std::find_if(costumes.begin(), costumes.end(), [value](auto& candidate) { + return candidate.scriptName == value.as_string(); + }); } else if (value.type() == DukValue::Type::NUMBER) { - auto newAnimationGroup = PeepAnimationGroup(value.as_uint() + EnumValue(PeepAnimationGroup::EntertainerPanda)); - if (availableCostumes.find(newAnimationGroup) != availableCostumes.end()) - { - peep->AnimationGroup = newAnimationGroup; - return; - } + // Find the peep's current costume by legacy number + auto target = PeepAnimationGroup(value.as_uint() + EnumValue(PeepAnimationGroup::EntertainerPanda)); + costume = std::find_if( + costumes.begin(), costumes.end(), [target](auto& candidate) { return candidate.legacyPosition == target; }); } - throw DukException() << "Invalid costume for this staff member"; + if (costume == costumes.end()) + throw DukException() << "Invalid costume for this staff member"; + + peep->AnimationObjectIndex = costume->objectId; + peep->AnimationGroup = costume->group; } std::shared_ptr ScStaff::patrolArea_get() const