1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2026-01-15 11:03:00 +01:00

Scripting: list and use available costumes dynamically

This commit is contained in:
Aaron van Geffen
2024-12-08 12:34:13 +01:00
parent e3c6fe22a6
commit dbc394cb67
4 changed files with 81 additions and 60 deletions

View File

@@ -170,6 +170,36 @@ namespace OpenRCT2
return !out.empty() ? out[0] : OBJECT_ENTRY_INDEX_NULL;
}
std::vector<AnimationGroupResult> getAnimationGroupsByPeepType(const AnimationPeepType type)
{
std::vector<AnimationGroupResult> groups{};
auto& objManager = GetContext()->GetObjectManager();
for (auto i = 0u; i < kMaxPeepAnimationsObjects; i++)
{
auto* animObj = objManager.GetLoadedObject<PeepAnimationsObject>(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)
{

View File

@@ -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<AnimationGroupResult> getAnimationGroupsByPeepType(const AnimationPeepType type);
SpriteBounds inferMaxAnimationDimensions(const PeepAnimation& anim);
} // namespace OpenRCT2

View File

@@ -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;

View File

@@ -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<PeepAnimationGroup> availableHandymanCostumes({
{ "handyman", PeepAnimationGroup::Handyman },
});
static const DukEnumMap<PeepAnimationGroup> availableMechanicCostumes({
{ "mechanic", PeepAnimationGroup::Mechanic },
});
static const DukEnumMap<PeepAnimationGroup> availableSecurityCostumes({
{ "security1", PeepAnimationGroup::Security },
{ "security2", PeepAnimationGroup::SecurityAlt },
});
static const DukEnumMap<PeepAnimationGroup> 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<PeepAnimationGroup>& costumesByStaffType(StaffType staffType)
static const std::vector<AnimationGroupResult> 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<ScPatrolArea> ScStaff::patrolArea_get() const