diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index d72a85892..9ef82ab7a 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -290,6 +290,9 @@ void PetComponent::Update(float deltaTime) { return; } + // Going back to the backpack, removed once the despawn effect has played + if (m_Despawning) return; + auto* owner = GetOwner(); if (owner == nullptr) { @@ -921,7 +924,19 @@ void PetComponent::Activate(Item* item, bool registerPet, bool fromTaming) { auto* owner = GetOwner(); if (owner == nullptr) return; - SetStatus(1); + + if (registerPet) { + // Summoned from the backpack. Live constructed the pet in the spawning state, so the client plays its + // spawn animation, played the pet's "despawn" effect (the circles and stars) and then cleared the state. + SetStatus(1 | PET_STATE_SPAWNING); + m_Parent->AddCallbackTimer(RenderComponent::GetAnimationTime(m_Parent, "spawn"), [this]() { + GameMessages::PlayFXEffect(m_Parent->GetObjectID(), -1, u"despawn", "").Send(UNASSIGNED_SYSTEM_ADDRESS); + SetStatus(m_Status & ~PET_STATE_SPAWNING); + Game::entityManager->SerializeEntity(m_Parent); + }); + } else { + SetStatus(1); + } auto databaseData = inventoryComponent->GetDatabasePet(m_DatabaseId); @@ -1047,11 +1062,20 @@ void PetComponent::AddDrainImaginationTimer(bool fromTaming) { } void PetComponent::Deactivate() { + if (m_Despawning) return; + m_Despawning = true; + GameMessages::PlayFXEffect(m_Parent->GetObjectID(), -1, u"despawn", "").Send(UNASSIGNED_SYSTEM_ADDRESS); activePets.erase(m_Owner); - m_Parent->Kill(); + auto* const movementAI = m_Parent->GetComponent(); + if (movementAI) movementAI->Stop(); + + // Live removed the pet only once its despawn effect had played; removing it at once cut the effect short + m_Parent->AddCallbackTimer(RenderComponent::GetAnimationTime(m_Parent, "despawn"), [this]() { + m_Parent->Kill(); + }); auto* owner = GetOwner(); diff --git a/dGame/dComponents/PetComponent.h b/dGame/dComponents/PetComponent.h index c7e19b0ef..8670de5f3 100644 --- a/dGame/dComponents/PetComponent.h +++ b/dGame/dComponents/PetComponent.h @@ -18,6 +18,13 @@ class PetComponent final : public Component public: static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::PET; + // Bits of the serialized pet status. State number N is bit 1 << (N - 1) (the client's StateNumToIndex, + // 0x00bd19b0); the meanings are what the live server sent (2014 packet captures) and what the client does with them. + + // State 8: a pet constructed with it plays its spawnAnim (LWOPetComponent::Deserialize, 0x00cd1270). Live + // constructed summoned pets with 0x84 and cleared this bit once the spawn animation was over. + static constexpr uint32_t PET_STATE_SPAWNING = 1 << 7; + explicit PetComponent(Entity* parentEntity, const int32_t componentID); ~PetComponent() override; @@ -344,6 +351,11 @@ private: * Pet information loaded from the CDClientDatabase */ CDPetComponent m_PetInfo; + + /** + * Set once the pet has been sent back to the backpack; it is removed after its despawn effect has played + */ + bool m_Despawning{ false }; }; #endif // !PETCOMPONENT_H