From 8270e0edfe1e8feafe963245dc5d8224ab0de477 Mon Sep 17 00:00:00 2001 From: jadebenn Date: Fri, 17 Nov 2023 19:29:42 -0600 Subject: [PATCH 01/73] Working on pet bouncers --- dGame/dComponents/BouncerComponent.cpp | 3 ++- dGame/dComponents/SwitchComponent.cpp | 13 +++++++++++-- dGame/dComponents/SwitchComponent.h | 2 ++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/dGame/dComponents/BouncerComponent.cpp b/dGame/dComponents/BouncerComponent.cpp index 56002ac40..49f6757fd 100644 --- a/dGame/dComponents/BouncerComponent.cpp +++ b/dGame/dComponents/BouncerComponent.cpp @@ -25,7 +25,8 @@ BouncerComponent::~BouncerComponent() { void BouncerComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) { outBitStream->Write(m_PetEnabled); if (m_PetEnabled) { - outBitStream->Write(m_PetBouncerEnabled); + LOG("NOPE."); + //outBitStream->Write(m_PetBouncerEnabled); } } diff --git a/dGame/dComponents/SwitchComponent.cpp b/dGame/dComponents/SwitchComponent.cpp index 41dff9f5d..9d8cd7bc7 100644 --- a/dGame/dComponents/SwitchComponent.cpp +++ b/dGame/dComponents/SwitchComponent.cpp @@ -101,6 +101,15 @@ void SwitchComponent::Update(float deltaTime) { } } +void SwitchComponent::OnUse(Entity* originator) { + LOG("YOU USED ME!"); + m_Parent->TriggerEvent(eTriggerEventType::INTERACT, originator); + + for (CppScripts::Script* script : CppScripts::GetEntityScripts(m_Parent)) { + script->OnUse(m_Parent, originator); + } +} + Entity* SwitchComponent::GetParentEntity() const { return m_Parent; } @@ -126,8 +135,8 @@ void SwitchComponent::SetPetBouncer(BouncerComponent* value) { m_PetBouncer = value; if (value != nullptr) { - m_PetBouncer->SetPetEnabled(true); - petSwitches.push_back(this); + //m_PetBouncer->SetPetEnabled(true); + //petSwitches.push_back(this); //This seems to govern if pets can "see" this } } diff --git a/dGame/dComponents/SwitchComponent.h b/dGame/dComponents/SwitchComponent.h index 25f694ba9..f27dc7b04 100644 --- a/dGame/dComponents/SwitchComponent.h +++ b/dGame/dComponents/SwitchComponent.h @@ -23,6 +23,8 @@ class SwitchComponent : public Component { void Update(float deltaTime) override; + void OnUse(Entity* originator) override; + Entity* GetParentEntity() const; void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override; From 5734ef85e80664899dc83099190ab604f9f310e8 Mon Sep 17 00:00:00 2001 From: jadebenn Date: Sat, 18 Nov 2023 18:43:47 -0600 Subject: [PATCH 02/73] initial dig functionality; need to clean up kruft --- dCommon/dEnums/ePetStatus.h | 18 ++++ dGame/dComponents/PetComponent.cpp | 95 +++++++++++++------ dGame/dComponents/PetComponent.h | 33 +++++++ dGame/dUtilities/SlashCommandHandler.cpp | 27 ++++++ .../02_server/Map/General/PetDigServer.cpp | 11 +++ dScripts/02_server/Map/General/PetDigServer.h | 7 ++ 6 files changed, 162 insertions(+), 29 deletions(-) create mode 100644 dCommon/dEnums/ePetStatus.h diff --git a/dCommon/dEnums/ePetStatus.h b/dCommon/dEnums/ePetStatus.h new file mode 100644 index 000000000..04c9c9b9d --- /dev/null +++ b/dCommon/dEnums/ePetStatus.h @@ -0,0 +1,18 @@ +#ifndef __EPETSTATUS__H__ +#define __EPETSTATUS__H__ + +#include + +enum ePetStatus : uint32_t { + NONE, + UNKNOWN1 = 0x1, + UNKNOWN2 = 0x2, + UNKNOWN3 = 0x4, + UNKNOWN4 = 0x8, + BEING_TAMED = 0x10, + IS_NOT_WAITING = 0x20, // Right name? - used to be decimal 20 + PLAY_SPAWN_ANIM = 0x80, + TAMEABLE = 0x4000000 +}; + +#endif //!__EPETSTATUS__H__ diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index 1780437ec..2d3643787 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -19,6 +19,7 @@ #include "ePetTamingNotifyType.h" #include "eUseItemResponse.h" #include "ePlayerFlag.h" +#include "ePetStatus.h" #include "Game.h" #include "dConfig.h" @@ -80,13 +81,16 @@ PetComponent::PetComponent(Entity* parent, uint32_t componentId): Component(pare m_Timer = 0; m_TimerAway = 0; m_DatabaseId = LWOOBJID_EMPTY; - m_Status = 67108866; // Tamable + m_Status = ePetStatus::TAMEABLE; // Tameable m_Ability = PetAbilityType::Invalid; m_StartPosition = NiPoint3::ZERO; m_MovementAI = nullptr; m_TresureTime = 0; m_Preconditions = nullptr; + m_ReadyToDig = false; + m_InInteract = false; + std::string checkPreconditions = GeneralUtils::UTF16ToWTF8(parent->GetVar(u"CheckPrecondition")); if (!checkPreconditions.empty()) { @@ -150,6 +154,15 @@ void PetComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpd } void PetComponent::OnUse(Entity* originator) { + LOG("PET USE!"); + + if(m_ReadyToDig) { + LOG("Dig initiated!"); + m_TresureTime = 2.0f; + //m_ReadyToDig = false; + SetAbility(PetAbilityType::DigAtPosition); + } + if (m_Owner != LWOOBJID_EMPTY) { return; } @@ -369,29 +382,8 @@ void PetComponent::Update(float deltaTime) { return; } - if (m_TresureTime > 0) { - auto* tresure = Game::entityManager->GetEntity(m_Interaction); - - if (tresure == nullptr) { - m_TresureTime = 0; - - return; - } - - m_TresureTime -= deltaTime; - - m_MovementAI->Stop(); - - if (m_TresureTime <= 0) { - m_Parent->SetOwnerOverride(m_Owner); - - tresure->Smash(m_Parent->GetObjectID()); - - m_Interaction = LWOOBJID_EMPTY; - - m_TresureTime = 0; - } - + if (m_TresureTime > 0.0f) { + InteractDig(deltaTime); return; } @@ -440,10 +432,9 @@ void PetComponent::Update(float deltaTime) { } } + // Determine if the "Lost Tags" mission has been completed and digging has been unlocked auto* missionComponent = owner->GetComponent(); if (!missionComponent) return; - - // Determine if the "Lost Tags" mission has been completed and digging has been unlocked const bool digUnlocked = missionComponent->GetMissionState(842) == eMissionState::COMPLETE; Entity* closestTresure = PetDigServer::GetClosestTresure(position); @@ -461,11 +452,14 @@ void PetComponent::Update(float deltaTime) { Command(NiPoint3::ZERO, LWOOBJID_EMPTY, 1, 202, true); - m_TresureTime = 2; + SetIsReadyToDig(true); + } else if (distance < 10 * 10) { haltDistance = 1; destination = tresurePosition; + + SetIsReadyToDig(false); } } @@ -480,6 +474,49 @@ void PetComponent::Update(float deltaTime) { m_Timer = 1; } +void PetComponent::SetIsReadyToDig(bool isReady) { + if (isReady) { + LOG("Dig state reached!"); + //m_Interaction = closestTresure->GetObjectID(); + SetAbility(PetAbilityType::JumpOnObject); + SetStatus(ePetStatus::IS_NOT_WAITING); // Treasure dig status + m_ReadyToDig = true; + } + else { + LOG("Dig state ended!"); + //m_Interaction = LWOOBJID_EMPTY; + SetAbility(PetAbilityType::Invalid); + SetStatus(0); // TODO: Check status + m_ReadyToDig = false; + } +} + +void PetComponent::InteractDig(float deltaTime) { //Should I rename to InteractDig? + LOG("Pet digging!"); + + auto* tresure = Game::entityManager->GetEntity(m_Interaction); + + if (tresure == nullptr) { + m_TresureTime = 0.0f; + return; + } + + m_TresureTime -= deltaTime; + + m_MovementAI->Stop(); + + if (m_TresureTime <= 0.0f) { + m_Parent->SetOwnerOverride(m_Owner); + + tresure->Smash(m_Parent->GetObjectID()); + + LOG("Pet dig completed!"); + m_Interaction = LWOOBJID_EMPTY; + m_TresureTime = 0.0f; + SetIsReadyToDig(false); + } +} + void PetComponent::TryBuild(uint32_t numBricks, bool clientFailed) { if (m_Tamer == LWOOBJID_EMPTY) return; @@ -736,7 +773,7 @@ void PetComponent::ClientExitTamingMinigame(bool voluntaryExit) { currentActivities.erase(m_Tamer); - SetStatus(67108866); + SetStatus(ePetStatus::TAMEABLE); m_Tamer = LWOOBJID_EMPTY; m_Timer = 0; @@ -787,7 +824,7 @@ void PetComponent::ClientFailTamingMinigame() { currentActivities.erase(m_Tamer); - SetStatus(67108866); + SetStatus(ePetStatus::TAMEABLE); m_Tamer = LWOOBJID_EMPTY; m_Timer = 0; diff --git a/dGame/dComponents/PetComponent.h b/dGame/dComponents/PetComponent.h index 980bb1462..341e592bd 100644 --- a/dGame/dComponents/PetComponent.h +++ b/dGame/dComponents/PetComponent.h @@ -29,6 +29,12 @@ class PetComponent : public Component void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override; void Update(float deltaTime) override; + /** + * Handles the pet dig interaction + * @param deltaTime time elapsed + */ + void InteractDig(float deltaTime); + /** * Handles an OnUse event from another entity, initializing the pet taming minigame if this pet is untamed. * @param originator the entity that triggered the event @@ -172,6 +178,23 @@ class PetComponent : public Component */ void SetPreconditions(std::string& conditions); + /** + * Sets if the pet is ready to dig + * @param isReady whether the pet is ready to dig (true) or not (false) + */ + void SetIsReadyToDig(bool isReady); + + /** + * @return is pet ready to dig + */ + bool GetIsReadyToDig() { return m_ReadyToDig; }; + + /** + * Sets pet's treasure timer + * @param digTime float representing the treasure dig time in seconds + */ + void SetTreasureTime(float digTime) { m_TresureTime = digTime; }; + /** * Returns the entity that this component belongs to * @return the entity that this component belongs to @@ -341,6 +364,16 @@ class PetComponent : public Component */ float m_TresureTime; + /** + * Boolean that sets if a pet is ready to dig and display the interact prompt + */ + bool m_ReadyToDig; + + /** + * Boolean that sets if a pet is in an interaction + */ + float m_InInteract; + /** * The position that this pet was spawned at */ diff --git a/dGame/dUtilities/SlashCommandHandler.cpp b/dGame/dUtilities/SlashCommandHandler.cpp index 71f0e38b6..96085ecdc 100644 --- a/dGame/dUtilities/SlashCommandHandler.cpp +++ b/dGame/dUtilities/SlashCommandHandler.cpp @@ -46,6 +46,7 @@ #include "dServer.h" #include "MissionComponent.h" #include "Mail.h" +#include "PetComponent.h" #include "dpWorld.h" #include "Item.h" #include "PropertyManagementComponent.h" @@ -679,6 +680,32 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit entity->GetCharacter()->SetPlayerFlag(flagId, false); } + if (chatCommand == "setpetstatus" && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER) { + if (args.size() == 0) { + ChatPackets::SendSystemMessage(sysAddr, u"Too few arguments!"); + return; + } + + uint32_t petStatus; + if (!GeneralUtils::TryParse(args[0], petStatus)) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid pet status!"); + return; + } + + // Determine if player has a pet summoned + auto* petComponent = PetComponent::GetActivePet(entity->GetObjectID()); + if (!petComponent) { + ChatPackets::SendSystemMessage(sysAddr, u"No active pet found!"); + return; + } + + petComponent->SetStatus(petStatus); + //Game::entityManager->SerializeEntity(petComponent->GetParentEntity()); + + std::u16string msg = u"Set pet status to " + (GeneralUtils::to_u16string(petStatus)); + ChatPackets::SendSystemMessage(sysAddr, msg); + } + if (chatCommand == "resetmission" && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER) { if (args.size() == 0) return; diff --git a/dScripts/02_server/Map/General/PetDigServer.cpp b/dScripts/02_server/Map/General/PetDigServer.cpp index 0ea78e4f4..5e67368e0 100644 --- a/dScripts/02_server/Map/General/PetDigServer.cpp +++ b/dScripts/02_server/Map/General/PetDigServer.cpp @@ -101,6 +101,17 @@ void PetDigServer::OnDie(Entity* self, Entity* killer) { } } +void PetDigServer::OnUse(Entity* self, Entity* user) { + LOG("Treasure used!"); + + auto* petComponent = PetComponent::GetActivePet(user->GetObjectID()); + if (!petComponent) return; + + if(petComponent->GetIsReadyToDig()) { + petComponent->SetTreasureTime(2.0f); + } +} + void PetDigServer::HandleXBuildDig(const Entity* self, Entity* owner, Entity* pet) { auto playerID = self->GetVar(u"builder"); if (playerID == LWOOBJID_EMPTY || playerID != owner->GetObjectID()) diff --git a/dScripts/02_server/Map/General/PetDigServer.h b/dScripts/02_server/Map/General/PetDigServer.h index 1122517b0..232214d8e 100644 --- a/dScripts/02_server/Map/General/PetDigServer.h +++ b/dScripts/02_server/Map/General/PetDigServer.h @@ -17,6 +17,13 @@ class PetDigServer : public CppScripts::Script void OnStartup(Entity* self) override; void OnDie(Entity* self, Entity* killer) override; + /** + * Invoked when a player interacts with treasure. + * @param self the entity the script belongs to + * @param user the entity that used the treasure + */ + void OnUse(Entity* self, Entity* user) override; + static Entity* GetClosestTresure(NiPoint3 position); private: From a76ddd9ae1b3ac9eee7184f70273f4fe7fcbb06c Mon Sep 17 00:00:00 2001 From: jadebenn Date: Sat, 18 Nov 2023 18:50:09 -0600 Subject: [PATCH 03/73] cleaned up kruft --- dGame/dComponents/BouncerComponent.cpp | 3 +-- dGame/dComponents/SwitchComponent.cpp | 13 ++----------- dGame/dComponents/SwitchComponent.h | 2 -- 3 files changed, 3 insertions(+), 15 deletions(-) diff --git a/dGame/dComponents/BouncerComponent.cpp b/dGame/dComponents/BouncerComponent.cpp index 49f6757fd..56002ac40 100644 --- a/dGame/dComponents/BouncerComponent.cpp +++ b/dGame/dComponents/BouncerComponent.cpp @@ -25,8 +25,7 @@ BouncerComponent::~BouncerComponent() { void BouncerComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) { outBitStream->Write(m_PetEnabled); if (m_PetEnabled) { - LOG("NOPE."); - //outBitStream->Write(m_PetBouncerEnabled); + outBitStream->Write(m_PetBouncerEnabled); } } diff --git a/dGame/dComponents/SwitchComponent.cpp b/dGame/dComponents/SwitchComponent.cpp index 9d8cd7bc7..41dff9f5d 100644 --- a/dGame/dComponents/SwitchComponent.cpp +++ b/dGame/dComponents/SwitchComponent.cpp @@ -101,15 +101,6 @@ void SwitchComponent::Update(float deltaTime) { } } -void SwitchComponent::OnUse(Entity* originator) { - LOG("YOU USED ME!"); - m_Parent->TriggerEvent(eTriggerEventType::INTERACT, originator); - - for (CppScripts::Script* script : CppScripts::GetEntityScripts(m_Parent)) { - script->OnUse(m_Parent, originator); - } -} - Entity* SwitchComponent::GetParentEntity() const { return m_Parent; } @@ -135,8 +126,8 @@ void SwitchComponent::SetPetBouncer(BouncerComponent* value) { m_PetBouncer = value; if (value != nullptr) { - //m_PetBouncer->SetPetEnabled(true); - //petSwitches.push_back(this); //This seems to govern if pets can "see" this + m_PetBouncer->SetPetEnabled(true); + petSwitches.push_back(this); } } diff --git a/dGame/dComponents/SwitchComponent.h b/dGame/dComponents/SwitchComponent.h index f27dc7b04..25f694ba9 100644 --- a/dGame/dComponents/SwitchComponent.h +++ b/dGame/dComponents/SwitchComponent.h @@ -23,8 +23,6 @@ class SwitchComponent : public Component { void Update(float deltaTime) override; - void OnUse(Entity* originator) override; - Entity* GetParentEntity() const; void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override; From bd6f9a7a3b6cf2848efcf992ecd61d5f0c3317e5 Mon Sep 17 00:00:00 2001 From: jadebenn Date: Sat, 18 Nov 2023 20:11:28 -0600 Subject: [PATCH 04/73] added imagination cost to digs --- dGame/dComponents/PetComponent.h | 2 +- dScripts/02_server/Map/General/PetDigServer.cpp | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/dGame/dComponents/PetComponent.h b/dGame/dComponents/PetComponent.h index 341e592bd..60038cb79 100644 --- a/dGame/dComponents/PetComponent.h +++ b/dGame/dComponents/PetComponent.h @@ -372,7 +372,7 @@ class PetComponent : public Component /** * Boolean that sets if a pet is in an interaction */ - float m_InInteract; + bool m_InInteract; /** * The position that this pet was spawned at diff --git a/dScripts/02_server/Map/General/PetDigServer.cpp b/dScripts/02_server/Map/General/PetDigServer.cpp index 5e67368e0..3aa62fce1 100644 --- a/dScripts/02_server/Map/General/PetDigServer.cpp +++ b/dScripts/02_server/Map/General/PetDigServer.cpp @@ -1,5 +1,6 @@ #include "dZoneManager.h" #include "PetDigServer.h" +#include "DestroyableComponent.h" #include "MissionComponent.h" #include "EntityManager.h" #include "Character.h" @@ -107,8 +108,16 @@ void PetDigServer::OnUse(Entity* self, Entity* user) { auto* petComponent = PetComponent::GetActivePet(user->GetObjectID()); if (!petComponent) return; - if(petComponent->GetIsReadyToDig()) { + if(petComponent->GetIsReadyToDig()) { // TODO: Add handling of the "first time" dig message petComponent->SetTreasureTime(2.0f); + + auto* destroyableComponent = user->GetComponent(); + if (!destroyableComponent) return; + + auto imagination = destroyableComponent->GetImagination(); + imagination -= 1; // TODO: Get rid of this magic number + destroyableComponent->SetImagination(imagination); + Game::entityManager->SerializeEntity(user); } } From 76d31871996bbe24d402be54964b598982937297 Mon Sep 17 00:00:00 2001 From: jadebenn Date: Sat, 18 Nov 2023 20:20:58 -0600 Subject: [PATCH 05/73] actually check if the player can afford to dig --- dScripts/02_server/Map/General/PetDigServer.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/dScripts/02_server/Map/General/PetDigServer.cpp b/dScripts/02_server/Map/General/PetDigServer.cpp index 3aa62fce1..bb0576271 100644 --- a/dScripts/02_server/Map/General/PetDigServer.cpp +++ b/dScripts/02_server/Map/General/PetDigServer.cpp @@ -109,12 +109,14 @@ void PetDigServer::OnUse(Entity* self, Entity* user) { if (!petComponent) return; if(petComponent->GetIsReadyToDig()) { // TODO: Add handling of the "first time" dig message - petComponent->SetTreasureTime(2.0f); - auto* destroyableComponent = user->GetComponent(); if (!destroyableComponent) return; auto imagination = destroyableComponent->GetImagination(); + if (imagination == 0) return; // TODO: Check if there was special behavior for this in the live game + + petComponent->SetTreasureTime(2.0f); // TODO: Get rid of this magic number + imagination -= 1; // TODO: Get rid of this magic number destroyableComponent->SetImagination(imagination); Game::entityManager->SerializeEntity(user); From c8b624c3dd313fa9c0457451392d403a7db6f03b Mon Sep 17 00:00:00 2001 From: jadebenn Date: Sun, 19 Nov 2023 14:06:28 -0600 Subject: [PATCH 06/73] minor changes --- dScripts/02_server/Map/General/PetDigServer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dScripts/02_server/Map/General/PetDigServer.cpp b/dScripts/02_server/Map/General/PetDigServer.cpp index bb0576271..d60710239 100644 --- a/dScripts/02_server/Map/General/PetDigServer.cpp +++ b/dScripts/02_server/Map/General/PetDigServer.cpp @@ -113,7 +113,7 @@ void PetDigServer::OnUse(Entity* self, Entity* user) { if (!destroyableComponent) return; auto imagination = destroyableComponent->GetImagination(); - if (imagination == 0) return; // TODO: Check if there was special behavior for this in the live game + if (imagination == 0) return; // TODO: Check if there was special behavior for this in the live game (PR_NEED_IMAGINATION) petComponent->SetTreasureTime(2.0f); // TODO: Get rid of this magic number From e835eb39660d43654d4a1f4400837ac0d74a821e Mon Sep 17 00:00:00 2001 From: jadebenn Date: Sun, 19 Nov 2023 16:46:27 -0600 Subject: [PATCH 07/73] Fixed pet emotes not playing --- dCommon/dEnums/ePetStatus.h | 18 ------ dGame/dComponents/PetComponent.cpp | 34 ++++++----- dGame/dComponents/PetComponent.h | 40 ++++++++++-- dGame/dGameMessages/GameMessages.cpp | 34 +++++------ dGame/dUtilities/SlashCommandHandler.cpp | 61 ++++++++++++++----- .../02_server/Map/General/PetDigServer.cpp | 2 +- docs/Commands.md | 1 + 7 files changed, 119 insertions(+), 71 deletions(-) delete mode 100644 dCommon/dEnums/ePetStatus.h diff --git a/dCommon/dEnums/ePetStatus.h b/dCommon/dEnums/ePetStatus.h deleted file mode 100644 index 04c9c9b9d..000000000 --- a/dCommon/dEnums/ePetStatus.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef __EPETSTATUS__H__ -#define __EPETSTATUS__H__ - -#include - -enum ePetStatus : uint32_t { - NONE, - UNKNOWN1 = 0x1, - UNKNOWN2 = 0x2, - UNKNOWN3 = 0x4, - UNKNOWN4 = 0x8, - BEING_TAMED = 0x10, - IS_NOT_WAITING = 0x20, // Right name? - used to be decimal 20 - PLAY_SPAWN_ANIM = 0x80, - TAMEABLE = 0x4000000 -}; - -#endif //!__EPETSTATUS__H__ diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index 2d3643787..7d8963164 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -19,7 +19,6 @@ #include "ePetTamingNotifyType.h" #include "eUseItemResponse.h" #include "ePlayerFlag.h" -#include "ePetStatus.h" #include "Game.h" #include "dConfig.h" @@ -81,7 +80,7 @@ PetComponent::PetComponent(Entity* parent, uint32_t componentId): Component(pare m_Timer = 0; m_TimerAway = 0; m_DatabaseId = LWOOBJID_EMPTY; - m_Status = ePetStatus::TAMEABLE; // Tameable + m_Status = PetStatus::TAMEABLE; // Tameable m_Ability = PetAbilityType::Invalid; m_StartPosition = NiPoint3::ZERO; m_MovementAI = nullptr; @@ -382,7 +381,7 @@ void PetComponent::Update(float deltaTime) { return; } - if (m_TresureTime > 0.0f) { + if (m_TresureTime > 0.0f) { //TODO: Find better trigger InteractDig(deltaTime); return; } @@ -450,7 +449,7 @@ void PetComponent::Update(float deltaTime) { if (distance < 5 * 5) { m_Interaction = closestTresure->GetObjectID(); - Command(NiPoint3::ZERO, LWOOBJID_EMPTY, 1, 202, true); + Command(NiPoint3::ZERO, LWOOBJID_EMPTY, 1, PetEmote::Bounce , true); // Plays 'bounce' animation SetIsReadyToDig(true); @@ -478,20 +477,20 @@ void PetComponent::SetIsReadyToDig(bool isReady) { if (isReady) { LOG("Dig state reached!"); //m_Interaction = closestTresure->GetObjectID(); - SetAbility(PetAbilityType::JumpOnObject); - SetStatus(ePetStatus::IS_NOT_WAITING); // Treasure dig status + //SetAbility(PetAbilityType::JumpOnObject); + SetStatus(PetStatus::IS_NOT_WAITING); // Treasure dig status m_ReadyToDig = true; } else { LOG("Dig state ended!"); //m_Interaction = LWOOBJID_EMPTY; - SetAbility(PetAbilityType::Invalid); + //SetAbility(PetAbilityType::Invalid); SetStatus(0); // TODO: Check status m_ReadyToDig = false; } } -void PetComponent::InteractDig(float deltaTime) { //Should I rename to InteractDig? +void PetComponent::InteractDig(float deltaTime) { LOG("Pet digging!"); auto* tresure = Game::entityManager->GetEntity(m_Interaction); @@ -773,7 +772,7 @@ void PetComponent::ClientExitTamingMinigame(bool voluntaryExit) { currentActivities.erase(m_Tamer); - SetStatus(ePetStatus::TAMEABLE); + SetStatus(PetStatus::TAMEABLE); m_Tamer = LWOOBJID_EMPTY; m_Timer = 0; @@ -824,7 +823,7 @@ void PetComponent::ClientFailTamingMinigame() { currentActivities.erase(m_Tamer); - SetStatus(ePetStatus::TAMEABLE); + SetStatus(PetStatus::TAMEABLE); m_Tamer = LWOOBJID_EMPTY; m_Timer = 0; @@ -1024,10 +1023,7 @@ void PetComponent::Release() { void PetComponent::Command(NiPoint3 position, LWOOBJID source, int32_t commandType, int32_t typeId, bool overrideObey) { auto* owner = GetOwner(); - - if (owner == nullptr) { - return; - } + if (!owner) return; if (commandType == 1) { // Emotes @@ -1151,3 +1147,13 @@ void PetComponent::LoadPetNameFromModeration() { void PetComponent::SetPreconditions(std::string& preconditions) { m_Preconditions = new PreconditionExpression(preconditions); } + +void PetComponent::StartInteractDig() { + //m_InInteract = true; + m_TresureTime = 2.0f; //TODO: Remove magic number + Command(NiPoint3::ZERO, LWOOBJID_EMPTY, 1, PetEmote::DigTreasure , true); +} + +void PetComponent::EndInteractDig() { + //m_InInteract = false; +} \ No newline at end of file diff --git a/dGame/dComponents/PetComponent.h b/dGame/dComponents/PetComponent.h index 60038cb79..4ec0ef29a 100644 --- a/dGame/dComponents/PetComponent.h +++ b/dGame/dComponents/PetComponent.h @@ -6,6 +6,24 @@ #include "Preconditions.h" #include "eReplicaComponentType.h" +enum PetStatus : uint32_t { + NONE, + UNKNOWN1 = 0x1, + UNKNOWN2 = 0x2, + UNKNOWN3 = 0x4, + UNKNOWN4 = 0x8, + BEING_TAMED = 0x10, + IS_NOT_WAITING = 0x20, // Right name? - used to be decimal 20 + PLAY_SPAWN_ANIM = 0x80, + TAMEABLE = 0x4000000 +}; + +enum PetEmote : int32_t { + ActivateSwitch = 201, + DigTreasure, + Bounce +}; + enum class PetAbilityType { Invalid, @@ -29,12 +47,6 @@ class PetComponent : public Component void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override; void Update(float deltaTime) override; - /** - * Handles the pet dig interaction - * @param deltaTime time elapsed - */ - void InteractDig(float deltaTime); - /** * Handles an OnUse event from another entity, initializing the pet taming minigame if this pet is untamed. * @param originator the entity that triggered the event @@ -189,6 +201,22 @@ class PetComponent : public Component */ bool GetIsReadyToDig() { return m_ReadyToDig; }; + /** + * Start the dig interaction + */ + void StartInteractDig(); + + /** + * Handles the pet dig interaction + * @param deltaTime time elapsed + */ + void InteractDig(float deltaTime); + + /** + * End the dig interaction + */ + void EndInteractDig(); + /** * Sets pet's treasure timer * @param digTime float representing the treasure dig time in seconds diff --git a/dGame/dGameMessages/GameMessages.cpp b/dGame/dGameMessages/GameMessages.cpp index 7fb9c259f..1a6d112b1 100644 --- a/dGame/dGameMessages/GameMessages.cpp +++ b/dGame/dGameMessages/GameMessages.cpp @@ -5017,28 +5017,28 @@ void GameMessages::HandlePlayEmote(RakNet::BitStream* inStream, Entity* entity) std::string sAnimationName = "deaded"; //Default name in case we fail to get the emote MissionComponent* missionComponent = entity->GetComponent(); - if (!missionComponent) return; - - if (targetID != LWOOBJID_EMPTY) { - auto* targetEntity = Game::entityManager->GetEntity(targetID); + if (missionComponent) { + if (targetID != LWOOBJID_EMPTY) { + auto* targetEntity = Game::entityManager->GetEntity(targetID); - LOG_DEBUG("Emote target found (%d)", targetEntity != nullptr); + LOG_DEBUG("Emote target found (%d)", targetEntity != nullptr); - if (targetEntity != nullptr) { - targetEntity->OnEmoteReceived(emoteID, entity); - missionComponent->Progress(eMissionTaskType::EMOTE, emoteID, targetID); - } - } else { - LOG_DEBUG("Target ID is empty, using backup"); - const auto scriptedEntities = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::SCRIPT); + if (targetEntity != nullptr) { + targetEntity->OnEmoteReceived(emoteID, entity); + missionComponent->Progress(eMissionTaskType::EMOTE, emoteID, targetID); + } + } else { + LOG_DEBUG("Target ID is empty, using backup"); + const auto scriptedEntities = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::SCRIPT); - const auto& referencePoint = entity->GetPosition(); + const auto& referencePoint = entity->GetPosition(); - for (auto* scripted : scriptedEntities) { - if (Vector3::DistanceSquared(scripted->GetPosition(), referencePoint) > 5.0f * 5.0f) continue; + for (auto* scripted : scriptedEntities) { + if (Vector3::DistanceSquared(scripted->GetPosition(), referencePoint) > 5.0f * 5.0f) continue; - scripted->OnEmoteReceived(emoteID, entity); - missionComponent->Progress(eMissionTaskType::EMOTE, emoteID, scripted->GetObjectID()); + scripted->OnEmoteReceived(emoteID, entity); + missionComponent->Progress(eMissionTaskType::EMOTE, emoteID, scripted->GetObjectID()); + } } } diff --git a/dGame/dUtilities/SlashCommandHandler.cpp b/dGame/dUtilities/SlashCommandHandler.cpp index 96085ecdc..fbda3b672 100644 --- a/dGame/dUtilities/SlashCommandHandler.cpp +++ b/dGame/dUtilities/SlashCommandHandler.cpp @@ -680,6 +680,34 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit entity->GetCharacter()->SetPlayerFlag(flagId, false); } + if (chatCommand == "resetmission" && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER) { + if (args.size() == 0) return; + + uint32_t missionID; + + if (!GeneralUtils::TryParse(args[0], missionID)) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid mission id."); + return; + } + + auto* comp = static_cast(entity->GetComponent(eReplicaComponentType::MISSION)); + + if (comp == nullptr) { + return; + } + + auto* mission = comp->GetMission(missionID); + + if (mission == nullptr) { + return; + } + + mission->SetMissionState(eMissionState::ACTIVE); + + return; + } + + // Pet status utility if (chatCommand == "setpetstatus" && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER) { if (args.size() == 0) { ChatPackets::SendSystemMessage(sysAddr, u"Too few arguments!"); @@ -706,33 +734,36 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit ChatPackets::SendSystemMessage(sysAddr, msg); } - if (chatCommand == "resetmission" && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER) { - if (args.size() == 0) return; - - uint32_t missionID; - - if (!GeneralUtils::TryParse(args[0], missionID)) { - ChatPackets::SendSystemMessage(sysAddr, u"Invalid mission id."); + //Pet command utility + if (chatCommand == "petcommand" && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER) { + if (args.size() < 2) { + ChatPackets::SendSystemMessage(sysAddr, u"Too few arguments!"); return; } - auto* comp = static_cast(entity->GetComponent(eReplicaComponentType::MISSION)); - - if (comp == nullptr) { + int32_t commandType; + if (!GeneralUtils::TryParse(args[0], commandType)) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid command type!"); return; } - auto* mission = comp->GetMission(missionID); - - if (mission == nullptr) { + int32_t typeId; + if (!GeneralUtils::TryParse(args[1], typeId)) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid command type id!"); return; } - mission->SetMissionState(eMissionState::ACTIVE); + // Determine if player has a pet summoned + auto* petComponent = PetComponent::GetActivePet(entity->GetObjectID()); + if (!petComponent) { + ChatPackets::SendSystemMessage(sysAddr, u"No active pet found!"); + return; + } - return; + petComponent->Command(NiPoint3::ZERO, LWOOBJID_EMPTY, commandType, typeId, true); } + if (chatCommand == "playeffect" && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER && args.size() >= 3) { int32_t effectID = 0; diff --git a/dScripts/02_server/Map/General/PetDigServer.cpp b/dScripts/02_server/Map/General/PetDigServer.cpp index d60710239..67109de8a 100644 --- a/dScripts/02_server/Map/General/PetDigServer.cpp +++ b/dScripts/02_server/Map/General/PetDigServer.cpp @@ -115,7 +115,7 @@ void PetDigServer::OnUse(Entity* self, Entity* user) { auto imagination = destroyableComponent->GetImagination(); if (imagination == 0) return; // TODO: Check if there was special behavior for this in the live game (PR_NEED_IMAGINATION) - petComponent->SetTreasureTime(2.0f); // TODO: Get rid of this magic number + petComponent->StartInteractDig(); imagination -= 1; // TODO: Get rid of this magic number destroyableComponent->SetImagination(imagination); diff --git a/docs/Commands.md b/docs/Commands.md index 71a3da432..b669175d9 100644 --- a/docs/Commands.md +++ b/docs/Commands.md @@ -81,6 +81,7 @@ These commands are primarily for development and testing. The usage of many of t |list-spawns|`/list-spawns`|Lists all the character spawn points in the zone. Additionally, this command will display the current scene that plays when the character lands in the next zone, if there is one.|8| |locrow|`/locrow`|Prints the your current position and rotation information to the console.|8| |lookup|`/lookup `|Searches through the Objects table in the client SQLite database for items whose display name, name, or description contains the query. Query can be multiple words delimited by spaces.|8| +|petcommand|`/petcommand `|Sends pet command to pet with given command type and type ID.|8| |playanimation|`/playanimation `|Plays animation with given ID. Alias: `/playanim`.|8| |playeffect|`/playeffect `|Plays an effect.|8| |playlvlfx|`/playlvlfx`|Plays the level up animation on your character.|8| From 23664c0a9b8eaedb116304a43e29a949529ae70a Mon Sep 17 00:00:00 2001 From: jadebenn Date: Sun, 19 Nov 2023 17:31:31 -0600 Subject: [PATCH 08/73] updated pet command functionality --- dGame/dComponents/PetComponent.cpp | 8 +++++- dGame/dUtilities/SlashCommandHandler.cpp | 31 +++++++++++++++++++++--- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index 7d8963164..1ab068337 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -381,7 +381,7 @@ void PetComponent::Update(float deltaTime) { return; } - if (m_TresureTime > 0.0f) { //TODO: Find better trigger + if (m_TresureTime > 0.0f) { //TODO: Find better trigger? InteractDig(deltaTime); return; } @@ -1037,6 +1037,12 @@ void PetComponent::Command(NiPoint3 position, LWOOBJID source, int32_t commandTy if (owner->GetGMLevel() >= eGameMasterLevel::DEVELOPER) { ChatPackets::SendSystemMessage(owner->GetSystemAddress(), u"Commmand Type: " + (GeneralUtils::to_u16string(commandType)) + u" - Type Id: " + (GeneralUtils::to_u16string(typeId))); } + + // Add movement functionality + if (position != NiPoint3::ZERO) { + m_MovementAI->SetDestination(position); + m_Timer = 9; //Is this setting how long until the next update tick? + } } LWOOBJID PetComponent::GetOwnerId() const { diff --git a/dGame/dUtilities/SlashCommandHandler.cpp b/dGame/dUtilities/SlashCommandHandler.cpp index fbda3b672..21f45cd2b 100644 --- a/dGame/dUtilities/SlashCommandHandler.cpp +++ b/dGame/dUtilities/SlashCommandHandler.cpp @@ -736,19 +736,19 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit //Pet command utility if (chatCommand == "petcommand" && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER) { - if (args.size() < 2) { + if (args.size() < 5) { ChatPackets::SendSystemMessage(sysAddr, u"Too few arguments!"); return; } int32_t commandType; - if (!GeneralUtils::TryParse(args[0], commandType)) { + if (!GeneralUtils::TryParse(args[0+3], commandType)) { ChatPackets::SendSystemMessage(sysAddr, u"Invalid command type!"); return; } int32_t typeId; - if (!GeneralUtils::TryParse(args[1], typeId)) { + if (!GeneralUtils::TryParse(args[1+3], typeId)) { ChatPackets::SendSystemMessage(sysAddr, u"Invalid command type id!"); return; } @@ -760,7 +760,30 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit return; } - petComponent->Command(NiPoint3::ZERO, LWOOBJID_EMPTY, commandType, typeId, true); + //Determine the positional coordinates + NiPoint3 commandPos{}; + float x, y, z; + if (!GeneralUtils::TryParse(args[0], x)) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid x."); + return; + } + + if (!GeneralUtils::TryParse(args[1], y)) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid y."); + return; + } + + if (!GeneralUtils::TryParse(args[2], z)) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid z."); + return; + } + + // Set command position + commandPos.SetX(x); + commandPos.SetY(y); + commandPos.SetZ(z); + + petComponent->Command(commandPos, entity->GetObjectID(), commandType, typeId, true); } From 81d8c187ea5233c88ea396be14195dc296aa7f1d Mon Sep 17 00:00:00 2001 From: jadebenn Date: Tue, 21 Nov 2023 20:16:17 -0600 Subject: [PATCH 09/73] mission fix redo --- dGame/dComponents/PetComponent.cpp | 9 ++--- dGame/dGameMessages/GameMessages.cpp | 50 ++++++++++++++-------------- 2 files changed, 27 insertions(+), 32 deletions(-) diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index 1ab068337..f979def40 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -368,15 +368,12 @@ void PetComponent::Update(float deltaTime) { } auto* owner = GetOwner(); - if (owner == nullptr) { m_Parent->Kill(); - return; } m_MovementAI = m_Parent->GetComponent(); - if (m_MovementAI == nullptr) { return; } @@ -390,7 +387,6 @@ void PetComponent::Update(float deltaTime) { NiPoint3 position = m_MovementAI->GetParent()->GetPosition(); float distanceToOwner = Vector3::DistanceSquared(position, destination); - if (distanceToOwner > 50 * 50 || m_TimerAway > 5) { m_MovementAI->Warp(destination); @@ -437,7 +433,6 @@ void PetComponent::Update(float deltaTime) { const bool digUnlocked = missionComponent->GetMissionState(842) == eMissionState::COMPLETE; Entity* closestTresure = PetDigServer::GetClosestTresure(position); - if (closestTresure != nullptr && digUnlocked) { // Skeleton Dragon Pat special case for bone digging if (closestTresure->GetLOT() == 12192 && m_Parent->GetLOT() != 13067) { @@ -449,7 +444,7 @@ void PetComponent::Update(float deltaTime) { if (distance < 5 * 5) { m_Interaction = closestTresure->GetObjectID(); - Command(NiPoint3::ZERO, LWOOBJID_EMPTY, 1, PetEmote::Bounce , true); // Plays 'bounce' animation + Command(NiPoint3::ZERO, LWOOBJID_EMPTY, 1, PetEmote::Bounce, true); // Plays 'bounce' animation SetIsReadyToDig(true); @@ -1157,7 +1152,7 @@ void PetComponent::SetPreconditions(std::string& preconditions) { void PetComponent::StartInteractDig() { //m_InInteract = true; m_TresureTime = 2.0f; //TODO: Remove magic number - Command(NiPoint3::ZERO, LWOOBJID_EMPTY, 1, PetEmote::DigTreasure , true); + Command(NiPoint3::ZERO, LWOOBJID_EMPTY, 1, PetEmote::DigTreasure, true); } void PetComponent::EndInteractDig() { diff --git a/dGame/dGameMessages/GameMessages.cpp b/dGame/dGameMessages/GameMessages.cpp index 1a6d112b1..4777631d2 100644 --- a/dGame/dGameMessages/GameMessages.cpp +++ b/dGame/dGameMessages/GameMessages.cpp @@ -5016,39 +5016,39 @@ void GameMessages::HandlePlayEmote(RakNet::BitStream* inStream, Entity* entity) if (emoteID == 0) return; std::string sAnimationName = "deaded"; //Default name in case we fail to get the emote - MissionComponent* missionComponent = entity->GetComponent(); - if (missionComponent) { - if (targetID != LWOOBJID_EMPTY) { - auto* targetEntity = Game::entityManager->GetEntity(targetID); + CDEmoteTableTable* emotes = CDClientManager::Instance().GetTable(); + if (emotes) { + CDEmoteTable* emote = emotes->GetEmote(emoteID); + if (emote) sAnimationName = emote->animationName; + } - LOG_DEBUG("Emote target found (%d)", targetEntity != nullptr); + RenderComponent::PlayAnimation(entity, sAnimationName); - if (targetEntity != nullptr) { - targetEntity->OnEmoteReceived(emoteID, entity); - missionComponent->Progress(eMissionTaskType::EMOTE, emoteID, targetID); - } - } else { - LOG_DEBUG("Target ID is empty, using backup"); - const auto scriptedEntities = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::SCRIPT); + MissionComponent* missionComponent = entity->GetComponent(); + if (!missionComponent) return; - const auto& referencePoint = entity->GetPosition(); + if (targetID != LWOOBJID_EMPTY) { + auto* targetEntity = Game::entityManager->GetEntity(targetID); - for (auto* scripted : scriptedEntities) { - if (Vector3::DistanceSquared(scripted->GetPosition(), referencePoint) > 5.0f * 5.0f) continue; + LOG_DEBUG("Emote target found (%d)", targetEntity != nullptr); - scripted->OnEmoteReceived(emoteID, entity); - missionComponent->Progress(eMissionTaskType::EMOTE, emoteID, scripted->GetObjectID()); - } + if (targetEntity != nullptr) { + targetEntity->OnEmoteReceived(emoteID, entity); + missionComponent->Progress(eMissionTaskType::EMOTE, emoteID, targetID); } - } + } else { + LOG_DEBUG("Target ID is empty, using backup"); + const auto scriptedEntities = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::SCRIPT); - CDEmoteTableTable* emotes = CDClientManager::Instance().GetTable(); - if (emotes) { - CDEmoteTable* emote = emotes->GetEmote(emoteID); - if (emote) sAnimationName = emote->animationName; - } + const auto& referencePoint = entity->GetPosition(); - RenderComponent::PlayAnimation(entity, sAnimationName); + for (auto* scripted : scriptedEntities) { + if (Vector3::DistanceSquared(scripted->GetPosition(), referencePoint) > 5.0f * 5.0f) continue; + + scripted->OnEmoteReceived(emoteID, entity); + missionComponent->Progress(eMissionTaskType::EMOTE, emoteID, scripted->GetObjectID()); + } + } } void GameMessages::HandleModularBuildConvertModel(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr) { From 57a1c05e98c3869164bb98e24a7c5ad480a699a0 Mon Sep 17 00:00:00 2001 From: jadebenn Date: Tue, 21 Nov 2023 21:19:30 -0600 Subject: [PATCH 10/73] update loop cleanup --- dGame/dComponents/PetComponent.cpp | 54 ++++++++++++++---------------- dGame/dComponents/PetComponent.h | 6 ++++ 2 files changed, 32 insertions(+), 28 deletions(-) diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index f979def40..de5a7ef38 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -341,42 +341,18 @@ void PetComponent::Update(float deltaTime) { } if (m_Owner == LWOOBJID_EMPTY) { - if (m_Tamer != LWOOBJID_EMPTY) { - if (m_Timer > 0) { - m_Timer -= deltaTime; - - if (m_Timer <= 0) { - m_Timer = 0; - - ClientFailTamingMinigame(); - } - } - } else { - if (m_Timer > 0) { - m_Timer -= deltaTime; - - if (m_Timer <= 0) { - Wander(); - Game::entityManager->SerializeEntity(m_Parent); - } - } else { - m_Timer = 5; - } - } - + UpdateUnowned(deltaTime); return; } auto* owner = GetOwner(); - if (owner == nullptr) { + if (!owner) { m_Parent->Kill(); return; } m_MovementAI = m_Parent->GetComponent(); - if (m_MovementAI == nullptr) { - return; - } + if (!m_MovementAI) return; if (m_TresureTime > 0.0f) { //TODO: Find better trigger? InteractDig(deltaTime); @@ -404,7 +380,6 @@ void PetComponent::Update(float deltaTime) { if (m_Timer > 0) { m_Timer -= deltaTime; - return; } @@ -468,6 +443,29 @@ void PetComponent::Update(float deltaTime) { m_Timer = 1; } +void PetComponent::UpdateUnowned(float deltaTime) { + if (m_Tamer != LWOOBJID_EMPTY) { + if (m_Timer > 0) { + m_Timer -= deltaTime; + + if (m_Timer <= 0) { + m_Timer = 0; + + ClientFailTamingMinigame(); + } + } + } + else { + if (m_Timer > 0) { + m_Timer -= deltaTime; + + if (m_Timer <= 0) Wander(); + } else { + m_Timer = 5; + } + } +} + void PetComponent::SetIsReadyToDig(bool isReady) { if (isReady) { LOG("Dig state reached!"); diff --git a/dGame/dComponents/PetComponent.h b/dGame/dComponents/PetComponent.h index 4ec0ef29a..57f4a01fb 100644 --- a/dGame/dComponents/PetComponent.h +++ b/dGame/dComponents/PetComponent.h @@ -47,6 +47,12 @@ class PetComponent : public Component void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override; void Update(float deltaTime) override; + /** + * Handles updates for unowned pets + * @param deltaTime time since last update + */ + void UpdateUnowned(float deltaTime); + /** * Handles an OnUse event from another entity, initializing the pet taming minigame if this pet is untamed. * @param originator the entity that triggered the event From 74047bcc9caad77e85447ef40bd6d9660e54206b Mon Sep 17 00:00:00 2001 From: jadebenn Date: Fri, 17 Nov 2023 19:29:42 -0600 Subject: [PATCH 11/73] Working on pet bouncers --- dGame/dComponents/BouncerComponent.cpp | 3 ++- dGame/dComponents/SwitchComponent.cpp | 13 +++++++++++-- dGame/dComponents/SwitchComponent.h | 2 ++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/dGame/dComponents/BouncerComponent.cpp b/dGame/dComponents/BouncerComponent.cpp index 56002ac40..49f6757fd 100644 --- a/dGame/dComponents/BouncerComponent.cpp +++ b/dGame/dComponents/BouncerComponent.cpp @@ -25,7 +25,8 @@ BouncerComponent::~BouncerComponent() { void BouncerComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) { outBitStream->Write(m_PetEnabled); if (m_PetEnabled) { - outBitStream->Write(m_PetBouncerEnabled); + LOG("NOPE."); + //outBitStream->Write(m_PetBouncerEnabled); } } diff --git a/dGame/dComponents/SwitchComponent.cpp b/dGame/dComponents/SwitchComponent.cpp index 41dff9f5d..9d8cd7bc7 100644 --- a/dGame/dComponents/SwitchComponent.cpp +++ b/dGame/dComponents/SwitchComponent.cpp @@ -101,6 +101,15 @@ void SwitchComponent::Update(float deltaTime) { } } +void SwitchComponent::OnUse(Entity* originator) { + LOG("YOU USED ME!"); + m_Parent->TriggerEvent(eTriggerEventType::INTERACT, originator); + + for (CppScripts::Script* script : CppScripts::GetEntityScripts(m_Parent)) { + script->OnUse(m_Parent, originator); + } +} + Entity* SwitchComponent::GetParentEntity() const { return m_Parent; } @@ -126,8 +135,8 @@ void SwitchComponent::SetPetBouncer(BouncerComponent* value) { m_PetBouncer = value; if (value != nullptr) { - m_PetBouncer->SetPetEnabled(true); - petSwitches.push_back(this); + //m_PetBouncer->SetPetEnabled(true); + //petSwitches.push_back(this); //This seems to govern if pets can "see" this } } diff --git a/dGame/dComponents/SwitchComponent.h b/dGame/dComponents/SwitchComponent.h index 25f694ba9..f27dc7b04 100644 --- a/dGame/dComponents/SwitchComponent.h +++ b/dGame/dComponents/SwitchComponent.h @@ -23,6 +23,8 @@ class SwitchComponent : public Component { void Update(float deltaTime) override; + void OnUse(Entity* originator) override; + Entity* GetParentEntity() const; void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override; From 119968a90c53a8397004302ddf9625c387c6f934 Mon Sep 17 00:00:00 2001 From: jadebenn Date: Sat, 18 Nov 2023 18:43:47 -0600 Subject: [PATCH 12/73] initial dig functionality; need to clean up kruft --- dCommon/dEnums/ePetStatus.h | 18 ++++ dGame/dComponents/PetComponent.cpp | 95 +++++++++++++------ dGame/dComponents/PetComponent.h | 33 +++++++ dGame/dUtilities/SlashCommandHandler.cpp | 27 ++++++ .../02_server/Map/General/PetDigServer.cpp | 11 +++ dScripts/02_server/Map/General/PetDigServer.h | 7 ++ 6 files changed, 162 insertions(+), 29 deletions(-) create mode 100644 dCommon/dEnums/ePetStatus.h diff --git a/dCommon/dEnums/ePetStatus.h b/dCommon/dEnums/ePetStatus.h new file mode 100644 index 000000000..04c9c9b9d --- /dev/null +++ b/dCommon/dEnums/ePetStatus.h @@ -0,0 +1,18 @@ +#ifndef __EPETSTATUS__H__ +#define __EPETSTATUS__H__ + +#include + +enum ePetStatus : uint32_t { + NONE, + UNKNOWN1 = 0x1, + UNKNOWN2 = 0x2, + UNKNOWN3 = 0x4, + UNKNOWN4 = 0x8, + BEING_TAMED = 0x10, + IS_NOT_WAITING = 0x20, // Right name? - used to be decimal 20 + PLAY_SPAWN_ANIM = 0x80, + TAMEABLE = 0x4000000 +}; + +#endif //!__EPETSTATUS__H__ diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index 1780437ec..2d3643787 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -19,6 +19,7 @@ #include "ePetTamingNotifyType.h" #include "eUseItemResponse.h" #include "ePlayerFlag.h" +#include "ePetStatus.h" #include "Game.h" #include "dConfig.h" @@ -80,13 +81,16 @@ PetComponent::PetComponent(Entity* parent, uint32_t componentId): Component(pare m_Timer = 0; m_TimerAway = 0; m_DatabaseId = LWOOBJID_EMPTY; - m_Status = 67108866; // Tamable + m_Status = ePetStatus::TAMEABLE; // Tameable m_Ability = PetAbilityType::Invalid; m_StartPosition = NiPoint3::ZERO; m_MovementAI = nullptr; m_TresureTime = 0; m_Preconditions = nullptr; + m_ReadyToDig = false; + m_InInteract = false; + std::string checkPreconditions = GeneralUtils::UTF16ToWTF8(parent->GetVar(u"CheckPrecondition")); if (!checkPreconditions.empty()) { @@ -150,6 +154,15 @@ void PetComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpd } void PetComponent::OnUse(Entity* originator) { + LOG("PET USE!"); + + if(m_ReadyToDig) { + LOG("Dig initiated!"); + m_TresureTime = 2.0f; + //m_ReadyToDig = false; + SetAbility(PetAbilityType::DigAtPosition); + } + if (m_Owner != LWOOBJID_EMPTY) { return; } @@ -369,29 +382,8 @@ void PetComponent::Update(float deltaTime) { return; } - if (m_TresureTime > 0) { - auto* tresure = Game::entityManager->GetEntity(m_Interaction); - - if (tresure == nullptr) { - m_TresureTime = 0; - - return; - } - - m_TresureTime -= deltaTime; - - m_MovementAI->Stop(); - - if (m_TresureTime <= 0) { - m_Parent->SetOwnerOverride(m_Owner); - - tresure->Smash(m_Parent->GetObjectID()); - - m_Interaction = LWOOBJID_EMPTY; - - m_TresureTime = 0; - } - + if (m_TresureTime > 0.0f) { + InteractDig(deltaTime); return; } @@ -440,10 +432,9 @@ void PetComponent::Update(float deltaTime) { } } + // Determine if the "Lost Tags" mission has been completed and digging has been unlocked auto* missionComponent = owner->GetComponent(); if (!missionComponent) return; - - // Determine if the "Lost Tags" mission has been completed and digging has been unlocked const bool digUnlocked = missionComponent->GetMissionState(842) == eMissionState::COMPLETE; Entity* closestTresure = PetDigServer::GetClosestTresure(position); @@ -461,11 +452,14 @@ void PetComponent::Update(float deltaTime) { Command(NiPoint3::ZERO, LWOOBJID_EMPTY, 1, 202, true); - m_TresureTime = 2; + SetIsReadyToDig(true); + } else if (distance < 10 * 10) { haltDistance = 1; destination = tresurePosition; + + SetIsReadyToDig(false); } } @@ -480,6 +474,49 @@ void PetComponent::Update(float deltaTime) { m_Timer = 1; } +void PetComponent::SetIsReadyToDig(bool isReady) { + if (isReady) { + LOG("Dig state reached!"); + //m_Interaction = closestTresure->GetObjectID(); + SetAbility(PetAbilityType::JumpOnObject); + SetStatus(ePetStatus::IS_NOT_WAITING); // Treasure dig status + m_ReadyToDig = true; + } + else { + LOG("Dig state ended!"); + //m_Interaction = LWOOBJID_EMPTY; + SetAbility(PetAbilityType::Invalid); + SetStatus(0); // TODO: Check status + m_ReadyToDig = false; + } +} + +void PetComponent::InteractDig(float deltaTime) { //Should I rename to InteractDig? + LOG("Pet digging!"); + + auto* tresure = Game::entityManager->GetEntity(m_Interaction); + + if (tresure == nullptr) { + m_TresureTime = 0.0f; + return; + } + + m_TresureTime -= deltaTime; + + m_MovementAI->Stop(); + + if (m_TresureTime <= 0.0f) { + m_Parent->SetOwnerOverride(m_Owner); + + tresure->Smash(m_Parent->GetObjectID()); + + LOG("Pet dig completed!"); + m_Interaction = LWOOBJID_EMPTY; + m_TresureTime = 0.0f; + SetIsReadyToDig(false); + } +} + void PetComponent::TryBuild(uint32_t numBricks, bool clientFailed) { if (m_Tamer == LWOOBJID_EMPTY) return; @@ -736,7 +773,7 @@ void PetComponent::ClientExitTamingMinigame(bool voluntaryExit) { currentActivities.erase(m_Tamer); - SetStatus(67108866); + SetStatus(ePetStatus::TAMEABLE); m_Tamer = LWOOBJID_EMPTY; m_Timer = 0; @@ -787,7 +824,7 @@ void PetComponent::ClientFailTamingMinigame() { currentActivities.erase(m_Tamer); - SetStatus(67108866); + SetStatus(ePetStatus::TAMEABLE); m_Tamer = LWOOBJID_EMPTY; m_Timer = 0; diff --git a/dGame/dComponents/PetComponent.h b/dGame/dComponents/PetComponent.h index 980bb1462..341e592bd 100644 --- a/dGame/dComponents/PetComponent.h +++ b/dGame/dComponents/PetComponent.h @@ -29,6 +29,12 @@ class PetComponent : public Component void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override; void Update(float deltaTime) override; + /** + * Handles the pet dig interaction + * @param deltaTime time elapsed + */ + void InteractDig(float deltaTime); + /** * Handles an OnUse event from another entity, initializing the pet taming minigame if this pet is untamed. * @param originator the entity that triggered the event @@ -172,6 +178,23 @@ class PetComponent : public Component */ void SetPreconditions(std::string& conditions); + /** + * Sets if the pet is ready to dig + * @param isReady whether the pet is ready to dig (true) or not (false) + */ + void SetIsReadyToDig(bool isReady); + + /** + * @return is pet ready to dig + */ + bool GetIsReadyToDig() { return m_ReadyToDig; }; + + /** + * Sets pet's treasure timer + * @param digTime float representing the treasure dig time in seconds + */ + void SetTreasureTime(float digTime) { m_TresureTime = digTime; }; + /** * Returns the entity that this component belongs to * @return the entity that this component belongs to @@ -341,6 +364,16 @@ class PetComponent : public Component */ float m_TresureTime; + /** + * Boolean that sets if a pet is ready to dig and display the interact prompt + */ + bool m_ReadyToDig; + + /** + * Boolean that sets if a pet is in an interaction + */ + float m_InInteract; + /** * The position that this pet was spawned at */ diff --git a/dGame/dUtilities/SlashCommandHandler.cpp b/dGame/dUtilities/SlashCommandHandler.cpp index 13fc3ded1..75af4c965 100644 --- a/dGame/dUtilities/SlashCommandHandler.cpp +++ b/dGame/dUtilities/SlashCommandHandler.cpp @@ -46,6 +46,7 @@ #include "dServer.h" #include "MissionComponent.h" #include "Mail.h" +#include "PetComponent.h" #include "dpWorld.h" #include "Item.h" #include "PropertyManagementComponent.h" @@ -696,6 +697,32 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit entity->GetCharacter()->SetPlayerFlag(flagId, false); } + if (chatCommand == "setpetstatus" && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER) { + if (args.size() == 0) { + ChatPackets::SendSystemMessage(sysAddr, u"Too few arguments!"); + return; + } + + uint32_t petStatus; + if (!GeneralUtils::TryParse(args[0], petStatus)) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid pet status!"); + return; + } + + // Determine if player has a pet summoned + auto* petComponent = PetComponent::GetActivePet(entity->GetObjectID()); + if (!petComponent) { + ChatPackets::SendSystemMessage(sysAddr, u"No active pet found!"); + return; + } + + petComponent->SetStatus(petStatus); + //Game::entityManager->SerializeEntity(petComponent->GetParentEntity()); + + std::u16string msg = u"Set pet status to " + (GeneralUtils::to_u16string(petStatus)); + ChatPackets::SendSystemMessage(sysAddr, msg); + } + if (chatCommand == "resetmission" && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER) { if (args.size() == 0) return; diff --git a/dScripts/02_server/Map/General/PetDigServer.cpp b/dScripts/02_server/Map/General/PetDigServer.cpp index 0ea78e4f4..5e67368e0 100644 --- a/dScripts/02_server/Map/General/PetDigServer.cpp +++ b/dScripts/02_server/Map/General/PetDigServer.cpp @@ -101,6 +101,17 @@ void PetDigServer::OnDie(Entity* self, Entity* killer) { } } +void PetDigServer::OnUse(Entity* self, Entity* user) { + LOG("Treasure used!"); + + auto* petComponent = PetComponent::GetActivePet(user->GetObjectID()); + if (!petComponent) return; + + if(petComponent->GetIsReadyToDig()) { + petComponent->SetTreasureTime(2.0f); + } +} + void PetDigServer::HandleXBuildDig(const Entity* self, Entity* owner, Entity* pet) { auto playerID = self->GetVar(u"builder"); if (playerID == LWOOBJID_EMPTY || playerID != owner->GetObjectID()) diff --git a/dScripts/02_server/Map/General/PetDigServer.h b/dScripts/02_server/Map/General/PetDigServer.h index 1122517b0..232214d8e 100644 --- a/dScripts/02_server/Map/General/PetDigServer.h +++ b/dScripts/02_server/Map/General/PetDigServer.h @@ -17,6 +17,13 @@ class PetDigServer : public CppScripts::Script void OnStartup(Entity* self) override; void OnDie(Entity* self, Entity* killer) override; + /** + * Invoked when a player interacts with treasure. + * @param self the entity the script belongs to + * @param user the entity that used the treasure + */ + void OnUse(Entity* self, Entity* user) override; + static Entity* GetClosestTresure(NiPoint3 position); private: From 86abbc121c377d7b9d7c6f84ccdacffbb503bc9a Mon Sep 17 00:00:00 2001 From: jadebenn Date: Sat, 18 Nov 2023 18:50:09 -0600 Subject: [PATCH 13/73] cleaned up kruft --- dGame/dComponents/BouncerComponent.cpp | 3 +-- dGame/dComponents/SwitchComponent.cpp | 13 ++----------- dGame/dComponents/SwitchComponent.h | 2 -- 3 files changed, 3 insertions(+), 15 deletions(-) diff --git a/dGame/dComponents/BouncerComponent.cpp b/dGame/dComponents/BouncerComponent.cpp index 49f6757fd..56002ac40 100644 --- a/dGame/dComponents/BouncerComponent.cpp +++ b/dGame/dComponents/BouncerComponent.cpp @@ -25,8 +25,7 @@ BouncerComponent::~BouncerComponent() { void BouncerComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) { outBitStream->Write(m_PetEnabled); if (m_PetEnabled) { - LOG("NOPE."); - //outBitStream->Write(m_PetBouncerEnabled); + outBitStream->Write(m_PetBouncerEnabled); } } diff --git a/dGame/dComponents/SwitchComponent.cpp b/dGame/dComponents/SwitchComponent.cpp index 9d8cd7bc7..41dff9f5d 100644 --- a/dGame/dComponents/SwitchComponent.cpp +++ b/dGame/dComponents/SwitchComponent.cpp @@ -101,15 +101,6 @@ void SwitchComponent::Update(float deltaTime) { } } -void SwitchComponent::OnUse(Entity* originator) { - LOG("YOU USED ME!"); - m_Parent->TriggerEvent(eTriggerEventType::INTERACT, originator); - - for (CppScripts::Script* script : CppScripts::GetEntityScripts(m_Parent)) { - script->OnUse(m_Parent, originator); - } -} - Entity* SwitchComponent::GetParentEntity() const { return m_Parent; } @@ -135,8 +126,8 @@ void SwitchComponent::SetPetBouncer(BouncerComponent* value) { m_PetBouncer = value; if (value != nullptr) { - //m_PetBouncer->SetPetEnabled(true); - //petSwitches.push_back(this); //This seems to govern if pets can "see" this + m_PetBouncer->SetPetEnabled(true); + petSwitches.push_back(this); } } diff --git a/dGame/dComponents/SwitchComponent.h b/dGame/dComponents/SwitchComponent.h index f27dc7b04..25f694ba9 100644 --- a/dGame/dComponents/SwitchComponent.h +++ b/dGame/dComponents/SwitchComponent.h @@ -23,8 +23,6 @@ class SwitchComponent : public Component { void Update(float deltaTime) override; - void OnUse(Entity* originator) override; - Entity* GetParentEntity() const; void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override; From b3762dac057312e973d5da08bab9a5ed4993816d Mon Sep 17 00:00:00 2001 From: jadebenn Date: Sat, 18 Nov 2023 20:11:28 -0600 Subject: [PATCH 14/73] added imagination cost to digs --- dGame/dComponents/PetComponent.h | 2 +- dScripts/02_server/Map/General/PetDigServer.cpp | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/dGame/dComponents/PetComponent.h b/dGame/dComponents/PetComponent.h index 341e592bd..60038cb79 100644 --- a/dGame/dComponents/PetComponent.h +++ b/dGame/dComponents/PetComponent.h @@ -372,7 +372,7 @@ class PetComponent : public Component /** * Boolean that sets if a pet is in an interaction */ - float m_InInteract; + bool m_InInteract; /** * The position that this pet was spawned at diff --git a/dScripts/02_server/Map/General/PetDigServer.cpp b/dScripts/02_server/Map/General/PetDigServer.cpp index 5e67368e0..3aa62fce1 100644 --- a/dScripts/02_server/Map/General/PetDigServer.cpp +++ b/dScripts/02_server/Map/General/PetDigServer.cpp @@ -1,5 +1,6 @@ #include "dZoneManager.h" #include "PetDigServer.h" +#include "DestroyableComponent.h" #include "MissionComponent.h" #include "EntityManager.h" #include "Character.h" @@ -107,8 +108,16 @@ void PetDigServer::OnUse(Entity* self, Entity* user) { auto* petComponent = PetComponent::GetActivePet(user->GetObjectID()); if (!petComponent) return; - if(petComponent->GetIsReadyToDig()) { + if(petComponent->GetIsReadyToDig()) { // TODO: Add handling of the "first time" dig message petComponent->SetTreasureTime(2.0f); + + auto* destroyableComponent = user->GetComponent(); + if (!destroyableComponent) return; + + auto imagination = destroyableComponent->GetImagination(); + imagination -= 1; // TODO: Get rid of this magic number + destroyableComponent->SetImagination(imagination); + Game::entityManager->SerializeEntity(user); } } From e73b2bfcb2ea140ca54b83d23f32eae58fa39406 Mon Sep 17 00:00:00 2001 From: jadebenn Date: Sat, 18 Nov 2023 20:20:58 -0600 Subject: [PATCH 15/73] actually check if the player can afford to dig --- dScripts/02_server/Map/General/PetDigServer.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/dScripts/02_server/Map/General/PetDigServer.cpp b/dScripts/02_server/Map/General/PetDigServer.cpp index 3aa62fce1..bb0576271 100644 --- a/dScripts/02_server/Map/General/PetDigServer.cpp +++ b/dScripts/02_server/Map/General/PetDigServer.cpp @@ -109,12 +109,14 @@ void PetDigServer::OnUse(Entity* self, Entity* user) { if (!petComponent) return; if(petComponent->GetIsReadyToDig()) { // TODO: Add handling of the "first time" dig message - petComponent->SetTreasureTime(2.0f); - auto* destroyableComponent = user->GetComponent(); if (!destroyableComponent) return; auto imagination = destroyableComponent->GetImagination(); + if (imagination == 0) return; // TODO: Check if there was special behavior for this in the live game + + petComponent->SetTreasureTime(2.0f); // TODO: Get rid of this magic number + imagination -= 1; // TODO: Get rid of this magic number destroyableComponent->SetImagination(imagination); Game::entityManager->SerializeEntity(user); From 3f1f51f99643d66d1fc8ea210a815a0c31232b42 Mon Sep 17 00:00:00 2001 From: jadebenn Date: Sun, 19 Nov 2023 14:06:28 -0600 Subject: [PATCH 16/73] minor changes --- dScripts/02_server/Map/General/PetDigServer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dScripts/02_server/Map/General/PetDigServer.cpp b/dScripts/02_server/Map/General/PetDigServer.cpp index bb0576271..d60710239 100644 --- a/dScripts/02_server/Map/General/PetDigServer.cpp +++ b/dScripts/02_server/Map/General/PetDigServer.cpp @@ -113,7 +113,7 @@ void PetDigServer::OnUse(Entity* self, Entity* user) { if (!destroyableComponent) return; auto imagination = destroyableComponent->GetImagination(); - if (imagination == 0) return; // TODO: Check if there was special behavior for this in the live game + if (imagination == 0) return; // TODO: Check if there was special behavior for this in the live game (PR_NEED_IMAGINATION) petComponent->SetTreasureTime(2.0f); // TODO: Get rid of this magic number From 1a74ed676bb2a7b618f273574b52c4ede0d0ad59 Mon Sep 17 00:00:00 2001 From: jadebenn Date: Sun, 19 Nov 2023 16:46:27 -0600 Subject: [PATCH 17/73] Fixed pet emotes not playing --- dCommon/dEnums/ePetStatus.h | 18 ------ dGame/dComponents/PetComponent.cpp | 34 ++++++----- dGame/dComponents/PetComponent.h | 40 ++++++++++-- dGame/dGameMessages/GameMessages.cpp | 34 +++++------ dGame/dUtilities/SlashCommandHandler.cpp | 61 ++++++++++++++----- .../02_server/Map/General/PetDigServer.cpp | 2 +- docs/Commands.md | 1 + 7 files changed, 119 insertions(+), 71 deletions(-) delete mode 100644 dCommon/dEnums/ePetStatus.h diff --git a/dCommon/dEnums/ePetStatus.h b/dCommon/dEnums/ePetStatus.h deleted file mode 100644 index 04c9c9b9d..000000000 --- a/dCommon/dEnums/ePetStatus.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef __EPETSTATUS__H__ -#define __EPETSTATUS__H__ - -#include - -enum ePetStatus : uint32_t { - NONE, - UNKNOWN1 = 0x1, - UNKNOWN2 = 0x2, - UNKNOWN3 = 0x4, - UNKNOWN4 = 0x8, - BEING_TAMED = 0x10, - IS_NOT_WAITING = 0x20, // Right name? - used to be decimal 20 - PLAY_SPAWN_ANIM = 0x80, - TAMEABLE = 0x4000000 -}; - -#endif //!__EPETSTATUS__H__ diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index 2d3643787..7d8963164 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -19,7 +19,6 @@ #include "ePetTamingNotifyType.h" #include "eUseItemResponse.h" #include "ePlayerFlag.h" -#include "ePetStatus.h" #include "Game.h" #include "dConfig.h" @@ -81,7 +80,7 @@ PetComponent::PetComponent(Entity* parent, uint32_t componentId): Component(pare m_Timer = 0; m_TimerAway = 0; m_DatabaseId = LWOOBJID_EMPTY; - m_Status = ePetStatus::TAMEABLE; // Tameable + m_Status = PetStatus::TAMEABLE; // Tameable m_Ability = PetAbilityType::Invalid; m_StartPosition = NiPoint3::ZERO; m_MovementAI = nullptr; @@ -382,7 +381,7 @@ void PetComponent::Update(float deltaTime) { return; } - if (m_TresureTime > 0.0f) { + if (m_TresureTime > 0.0f) { //TODO: Find better trigger InteractDig(deltaTime); return; } @@ -450,7 +449,7 @@ void PetComponent::Update(float deltaTime) { if (distance < 5 * 5) { m_Interaction = closestTresure->GetObjectID(); - Command(NiPoint3::ZERO, LWOOBJID_EMPTY, 1, 202, true); + Command(NiPoint3::ZERO, LWOOBJID_EMPTY, 1, PetEmote::Bounce , true); // Plays 'bounce' animation SetIsReadyToDig(true); @@ -478,20 +477,20 @@ void PetComponent::SetIsReadyToDig(bool isReady) { if (isReady) { LOG("Dig state reached!"); //m_Interaction = closestTresure->GetObjectID(); - SetAbility(PetAbilityType::JumpOnObject); - SetStatus(ePetStatus::IS_NOT_WAITING); // Treasure dig status + //SetAbility(PetAbilityType::JumpOnObject); + SetStatus(PetStatus::IS_NOT_WAITING); // Treasure dig status m_ReadyToDig = true; } else { LOG("Dig state ended!"); //m_Interaction = LWOOBJID_EMPTY; - SetAbility(PetAbilityType::Invalid); + //SetAbility(PetAbilityType::Invalid); SetStatus(0); // TODO: Check status m_ReadyToDig = false; } } -void PetComponent::InteractDig(float deltaTime) { //Should I rename to InteractDig? +void PetComponent::InteractDig(float deltaTime) { LOG("Pet digging!"); auto* tresure = Game::entityManager->GetEntity(m_Interaction); @@ -773,7 +772,7 @@ void PetComponent::ClientExitTamingMinigame(bool voluntaryExit) { currentActivities.erase(m_Tamer); - SetStatus(ePetStatus::TAMEABLE); + SetStatus(PetStatus::TAMEABLE); m_Tamer = LWOOBJID_EMPTY; m_Timer = 0; @@ -824,7 +823,7 @@ void PetComponent::ClientFailTamingMinigame() { currentActivities.erase(m_Tamer); - SetStatus(ePetStatus::TAMEABLE); + SetStatus(PetStatus::TAMEABLE); m_Tamer = LWOOBJID_EMPTY; m_Timer = 0; @@ -1024,10 +1023,7 @@ void PetComponent::Release() { void PetComponent::Command(NiPoint3 position, LWOOBJID source, int32_t commandType, int32_t typeId, bool overrideObey) { auto* owner = GetOwner(); - - if (owner == nullptr) { - return; - } + if (!owner) return; if (commandType == 1) { // Emotes @@ -1151,3 +1147,13 @@ void PetComponent::LoadPetNameFromModeration() { void PetComponent::SetPreconditions(std::string& preconditions) { m_Preconditions = new PreconditionExpression(preconditions); } + +void PetComponent::StartInteractDig() { + //m_InInteract = true; + m_TresureTime = 2.0f; //TODO: Remove magic number + Command(NiPoint3::ZERO, LWOOBJID_EMPTY, 1, PetEmote::DigTreasure , true); +} + +void PetComponent::EndInteractDig() { + //m_InInteract = false; +} \ No newline at end of file diff --git a/dGame/dComponents/PetComponent.h b/dGame/dComponents/PetComponent.h index 60038cb79..4ec0ef29a 100644 --- a/dGame/dComponents/PetComponent.h +++ b/dGame/dComponents/PetComponent.h @@ -6,6 +6,24 @@ #include "Preconditions.h" #include "eReplicaComponentType.h" +enum PetStatus : uint32_t { + NONE, + UNKNOWN1 = 0x1, + UNKNOWN2 = 0x2, + UNKNOWN3 = 0x4, + UNKNOWN4 = 0x8, + BEING_TAMED = 0x10, + IS_NOT_WAITING = 0x20, // Right name? - used to be decimal 20 + PLAY_SPAWN_ANIM = 0x80, + TAMEABLE = 0x4000000 +}; + +enum PetEmote : int32_t { + ActivateSwitch = 201, + DigTreasure, + Bounce +}; + enum class PetAbilityType { Invalid, @@ -29,12 +47,6 @@ class PetComponent : public Component void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override; void Update(float deltaTime) override; - /** - * Handles the pet dig interaction - * @param deltaTime time elapsed - */ - void InteractDig(float deltaTime); - /** * Handles an OnUse event from another entity, initializing the pet taming minigame if this pet is untamed. * @param originator the entity that triggered the event @@ -189,6 +201,22 @@ class PetComponent : public Component */ bool GetIsReadyToDig() { return m_ReadyToDig; }; + /** + * Start the dig interaction + */ + void StartInteractDig(); + + /** + * Handles the pet dig interaction + * @param deltaTime time elapsed + */ + void InteractDig(float deltaTime); + + /** + * End the dig interaction + */ + void EndInteractDig(); + /** * Sets pet's treasure timer * @param digTime float representing the treasure dig time in seconds diff --git a/dGame/dGameMessages/GameMessages.cpp b/dGame/dGameMessages/GameMessages.cpp index 4cddebffa..39634a287 100644 --- a/dGame/dGameMessages/GameMessages.cpp +++ b/dGame/dGameMessages/GameMessages.cpp @@ -5018,28 +5018,28 @@ void GameMessages::HandlePlayEmote(RakNet::BitStream* inStream, Entity* entity) std::string sAnimationName = "deaded"; //Default name in case we fail to get the emote MissionComponent* missionComponent = entity->GetComponent(); - if (!missionComponent) return; - - if (targetID != LWOOBJID_EMPTY) { - auto* targetEntity = Game::entityManager->GetEntity(targetID); + if (missionComponent) { + if (targetID != LWOOBJID_EMPTY) { + auto* targetEntity = Game::entityManager->GetEntity(targetID); - LOG_DEBUG("Emote target found (%d)", targetEntity != nullptr); + LOG_DEBUG("Emote target found (%d)", targetEntity != nullptr); - if (targetEntity != nullptr) { - targetEntity->OnEmoteReceived(emoteID, entity); - missionComponent->Progress(eMissionTaskType::EMOTE, emoteID, targetID); - } - } else { - LOG_DEBUG("Target ID is empty, using backup"); - const auto scriptedEntities = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::SCRIPT); + if (targetEntity != nullptr) { + targetEntity->OnEmoteReceived(emoteID, entity); + missionComponent->Progress(eMissionTaskType::EMOTE, emoteID, targetID); + } + } else { + LOG_DEBUG("Target ID is empty, using backup"); + const auto scriptedEntities = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::SCRIPT); - const auto& referencePoint = entity->GetPosition(); + const auto& referencePoint = entity->GetPosition(); - for (auto* scripted : scriptedEntities) { - if (Vector3::DistanceSquared(scripted->GetPosition(), referencePoint) > 5.0f * 5.0f) continue; + for (auto* scripted : scriptedEntities) { + if (Vector3::DistanceSquared(scripted->GetPosition(), referencePoint) > 5.0f * 5.0f) continue; - scripted->OnEmoteReceived(emoteID, entity); - missionComponent->Progress(eMissionTaskType::EMOTE, emoteID, scripted->GetObjectID()); + scripted->OnEmoteReceived(emoteID, entity); + missionComponent->Progress(eMissionTaskType::EMOTE, emoteID, scripted->GetObjectID()); + } } } diff --git a/dGame/dUtilities/SlashCommandHandler.cpp b/dGame/dUtilities/SlashCommandHandler.cpp index 75af4c965..49eb056e4 100644 --- a/dGame/dUtilities/SlashCommandHandler.cpp +++ b/dGame/dUtilities/SlashCommandHandler.cpp @@ -697,6 +697,34 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit entity->GetCharacter()->SetPlayerFlag(flagId, false); } + if (chatCommand == "resetmission" && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER) { + if (args.size() == 0) return; + + uint32_t missionID; + + if (!GeneralUtils::TryParse(args[0], missionID)) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid mission id."); + return; + } + + auto* comp = static_cast(entity->GetComponent(eReplicaComponentType::MISSION)); + + if (comp == nullptr) { + return; + } + + auto* mission = comp->GetMission(missionID); + + if (mission == nullptr) { + return; + } + + mission->SetMissionState(eMissionState::ACTIVE); + + return; + } + + // Pet status utility if (chatCommand == "setpetstatus" && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER) { if (args.size() == 0) { ChatPackets::SendSystemMessage(sysAddr, u"Too few arguments!"); @@ -723,33 +751,36 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit ChatPackets::SendSystemMessage(sysAddr, msg); } - if (chatCommand == "resetmission" && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER) { - if (args.size() == 0) return; - - uint32_t missionID; - - if (!GeneralUtils::TryParse(args[0], missionID)) { - ChatPackets::SendSystemMessage(sysAddr, u"Invalid mission id."); + //Pet command utility + if (chatCommand == "petcommand" && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER) { + if (args.size() < 2) { + ChatPackets::SendSystemMessage(sysAddr, u"Too few arguments!"); return; } - auto* comp = static_cast(entity->GetComponent(eReplicaComponentType::MISSION)); - - if (comp == nullptr) { + int32_t commandType; + if (!GeneralUtils::TryParse(args[0], commandType)) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid command type!"); return; } - auto* mission = comp->GetMission(missionID); - - if (mission == nullptr) { + int32_t typeId; + if (!GeneralUtils::TryParse(args[1], typeId)) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid command type id!"); return; } - mission->SetMissionState(eMissionState::ACTIVE); + // Determine if player has a pet summoned + auto* petComponent = PetComponent::GetActivePet(entity->GetObjectID()); + if (!petComponent) { + ChatPackets::SendSystemMessage(sysAddr, u"No active pet found!"); + return; + } - return; + petComponent->Command(NiPoint3::ZERO, LWOOBJID_EMPTY, commandType, typeId, true); } + if (chatCommand == "playeffect" && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER && args.size() >= 3) { int32_t effectID = 0; diff --git a/dScripts/02_server/Map/General/PetDigServer.cpp b/dScripts/02_server/Map/General/PetDigServer.cpp index d60710239..67109de8a 100644 --- a/dScripts/02_server/Map/General/PetDigServer.cpp +++ b/dScripts/02_server/Map/General/PetDigServer.cpp @@ -115,7 +115,7 @@ void PetDigServer::OnUse(Entity* self, Entity* user) { auto imagination = destroyableComponent->GetImagination(); if (imagination == 0) return; // TODO: Check if there was special behavior for this in the live game (PR_NEED_IMAGINATION) - petComponent->SetTreasureTime(2.0f); // TODO: Get rid of this magic number + petComponent->StartInteractDig(); imagination -= 1; // TODO: Get rid of this magic number destroyableComponent->SetImagination(imagination); diff --git a/docs/Commands.md b/docs/Commands.md index 0ba7d86ea..1312ede09 100644 --- a/docs/Commands.md +++ b/docs/Commands.md @@ -82,6 +82,7 @@ These commands are primarily for development and testing. The usage of many of t |list-spawns|`/list-spawns`|Lists all the character spawn points in the zone. Additionally, this command will display the current scene that plays when the character lands in the next zone, if there is one.|8| |locrow|`/locrow`|Prints the your current position and rotation information to the console.|8| |lookup|`/lookup `|Searches through the Objects table in the client SQLite database for items whose display name, name, or description contains the query. Query can be multiple words delimited by spaces.|8| +|petcommand|`/petcommand `|Sends pet command to pet with given command type and type ID.|8| |playanimation|`/playanimation `|Plays animation with given ID. Alias: `/playanim`.|8| |playeffect|`/playeffect `|Plays an effect.|8| |playlvlfx|`/playlvlfx`|Plays the level up animation on your character.|8| From cb65de61ca50e76544265044ae30045102e452db Mon Sep 17 00:00:00 2001 From: jadebenn Date: Sun, 19 Nov 2023 17:31:31 -0600 Subject: [PATCH 18/73] updated pet command functionality --- dGame/dComponents/PetComponent.cpp | 8 +++++- dGame/dUtilities/SlashCommandHandler.cpp | 31 +++++++++++++++++++++--- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index 7d8963164..1ab068337 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -381,7 +381,7 @@ void PetComponent::Update(float deltaTime) { return; } - if (m_TresureTime > 0.0f) { //TODO: Find better trigger + if (m_TresureTime > 0.0f) { //TODO: Find better trigger? InteractDig(deltaTime); return; } @@ -1037,6 +1037,12 @@ void PetComponent::Command(NiPoint3 position, LWOOBJID source, int32_t commandTy if (owner->GetGMLevel() >= eGameMasterLevel::DEVELOPER) { ChatPackets::SendSystemMessage(owner->GetSystemAddress(), u"Commmand Type: " + (GeneralUtils::to_u16string(commandType)) + u" - Type Id: " + (GeneralUtils::to_u16string(typeId))); } + + // Add movement functionality + if (position != NiPoint3::ZERO) { + m_MovementAI->SetDestination(position); + m_Timer = 9; //Is this setting how long until the next update tick? + } } LWOOBJID PetComponent::GetOwnerId() const { diff --git a/dGame/dUtilities/SlashCommandHandler.cpp b/dGame/dUtilities/SlashCommandHandler.cpp index 49eb056e4..8268064af 100644 --- a/dGame/dUtilities/SlashCommandHandler.cpp +++ b/dGame/dUtilities/SlashCommandHandler.cpp @@ -753,19 +753,19 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit //Pet command utility if (chatCommand == "petcommand" && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER) { - if (args.size() < 2) { + if (args.size() < 5) { ChatPackets::SendSystemMessage(sysAddr, u"Too few arguments!"); return; } int32_t commandType; - if (!GeneralUtils::TryParse(args[0], commandType)) { + if (!GeneralUtils::TryParse(args[0+3], commandType)) { ChatPackets::SendSystemMessage(sysAddr, u"Invalid command type!"); return; } int32_t typeId; - if (!GeneralUtils::TryParse(args[1], typeId)) { + if (!GeneralUtils::TryParse(args[1+3], typeId)) { ChatPackets::SendSystemMessage(sysAddr, u"Invalid command type id!"); return; } @@ -777,7 +777,30 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit return; } - petComponent->Command(NiPoint3::ZERO, LWOOBJID_EMPTY, commandType, typeId, true); + //Determine the positional coordinates + NiPoint3 commandPos{}; + float x, y, z; + if (!GeneralUtils::TryParse(args[0], x)) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid x."); + return; + } + + if (!GeneralUtils::TryParse(args[1], y)) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid y."); + return; + } + + if (!GeneralUtils::TryParse(args[2], z)) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid z."); + return; + } + + // Set command position + commandPos.SetX(x); + commandPos.SetY(y); + commandPos.SetZ(z); + + petComponent->Command(commandPos, entity->GetObjectID(), commandType, typeId, true); } From 3f1468ebdfa6e93ab8dd326793aeebd445d3c663 Mon Sep 17 00:00:00 2001 From: jadebenn Date: Tue, 21 Nov 2023 20:16:17 -0600 Subject: [PATCH 19/73] mission fix redo --- dGame/dComponents/PetComponent.cpp | 9 ++--- dGame/dGameMessages/GameMessages.cpp | 50 ++++++++++++++-------------- 2 files changed, 27 insertions(+), 32 deletions(-) diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index 1ab068337..f979def40 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -368,15 +368,12 @@ void PetComponent::Update(float deltaTime) { } auto* owner = GetOwner(); - if (owner == nullptr) { m_Parent->Kill(); - return; } m_MovementAI = m_Parent->GetComponent(); - if (m_MovementAI == nullptr) { return; } @@ -390,7 +387,6 @@ void PetComponent::Update(float deltaTime) { NiPoint3 position = m_MovementAI->GetParent()->GetPosition(); float distanceToOwner = Vector3::DistanceSquared(position, destination); - if (distanceToOwner > 50 * 50 || m_TimerAway > 5) { m_MovementAI->Warp(destination); @@ -437,7 +433,6 @@ void PetComponent::Update(float deltaTime) { const bool digUnlocked = missionComponent->GetMissionState(842) == eMissionState::COMPLETE; Entity* closestTresure = PetDigServer::GetClosestTresure(position); - if (closestTresure != nullptr && digUnlocked) { // Skeleton Dragon Pat special case for bone digging if (closestTresure->GetLOT() == 12192 && m_Parent->GetLOT() != 13067) { @@ -449,7 +444,7 @@ void PetComponent::Update(float deltaTime) { if (distance < 5 * 5) { m_Interaction = closestTresure->GetObjectID(); - Command(NiPoint3::ZERO, LWOOBJID_EMPTY, 1, PetEmote::Bounce , true); // Plays 'bounce' animation + Command(NiPoint3::ZERO, LWOOBJID_EMPTY, 1, PetEmote::Bounce, true); // Plays 'bounce' animation SetIsReadyToDig(true); @@ -1157,7 +1152,7 @@ void PetComponent::SetPreconditions(std::string& preconditions) { void PetComponent::StartInteractDig() { //m_InInteract = true; m_TresureTime = 2.0f; //TODO: Remove magic number - Command(NiPoint3::ZERO, LWOOBJID_EMPTY, 1, PetEmote::DigTreasure , true); + Command(NiPoint3::ZERO, LWOOBJID_EMPTY, 1, PetEmote::DigTreasure, true); } void PetComponent::EndInteractDig() { diff --git a/dGame/dGameMessages/GameMessages.cpp b/dGame/dGameMessages/GameMessages.cpp index 39634a287..34b031d39 100644 --- a/dGame/dGameMessages/GameMessages.cpp +++ b/dGame/dGameMessages/GameMessages.cpp @@ -5017,39 +5017,39 @@ void GameMessages::HandlePlayEmote(RakNet::BitStream* inStream, Entity* entity) if (emoteID == 0) return; std::string sAnimationName = "deaded"; //Default name in case we fail to get the emote - MissionComponent* missionComponent = entity->GetComponent(); - if (missionComponent) { - if (targetID != LWOOBJID_EMPTY) { - auto* targetEntity = Game::entityManager->GetEntity(targetID); + CDEmoteTableTable* emotes = CDClientManager::Instance().GetTable(); + if (emotes) { + CDEmoteTable* emote = emotes->GetEmote(emoteID); + if (emote) sAnimationName = emote->animationName; + } - LOG_DEBUG("Emote target found (%d)", targetEntity != nullptr); + RenderComponent::PlayAnimation(entity, sAnimationName); - if (targetEntity != nullptr) { - targetEntity->OnEmoteReceived(emoteID, entity); - missionComponent->Progress(eMissionTaskType::EMOTE, emoteID, targetID); - } - } else { - LOG_DEBUG("Target ID is empty, using backup"); - const auto scriptedEntities = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::SCRIPT); + MissionComponent* missionComponent = entity->GetComponent(); + if (!missionComponent) return; - const auto& referencePoint = entity->GetPosition(); + if (targetID != LWOOBJID_EMPTY) { + auto* targetEntity = Game::entityManager->GetEntity(targetID); - for (auto* scripted : scriptedEntities) { - if (Vector3::DistanceSquared(scripted->GetPosition(), referencePoint) > 5.0f * 5.0f) continue; + LOG_DEBUG("Emote target found (%d)", targetEntity != nullptr); - scripted->OnEmoteReceived(emoteID, entity); - missionComponent->Progress(eMissionTaskType::EMOTE, emoteID, scripted->GetObjectID()); - } + if (targetEntity != nullptr) { + targetEntity->OnEmoteReceived(emoteID, entity); + missionComponent->Progress(eMissionTaskType::EMOTE, emoteID, targetID); } - } + } else { + LOG_DEBUG("Target ID is empty, using backup"); + const auto scriptedEntities = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::SCRIPT); - CDEmoteTableTable* emotes = CDClientManager::Instance().GetTable(); - if (emotes) { - CDEmoteTable* emote = emotes->GetEmote(emoteID); - if (emote) sAnimationName = emote->animationName; - } + const auto& referencePoint = entity->GetPosition(); - RenderComponent::PlayAnimation(entity, sAnimationName); + for (auto* scripted : scriptedEntities) { + if (Vector3::DistanceSquared(scripted->GetPosition(), referencePoint) > 5.0f * 5.0f) continue; + + scripted->OnEmoteReceived(emoteID, entity); + missionComponent->Progress(eMissionTaskType::EMOTE, emoteID, scripted->GetObjectID()); + } + } } void GameMessages::HandleModularBuildConvertModel(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr) { From e4b3e22570c96b0aad51a524a8ca2c8141334b9e Mon Sep 17 00:00:00 2001 From: jadebenn Date: Tue, 21 Nov 2023 21:19:30 -0600 Subject: [PATCH 20/73] update loop cleanup --- dGame/dComponents/PetComponent.cpp | 54 ++++++++++++++---------------- dGame/dComponents/PetComponent.h | 6 ++++ 2 files changed, 32 insertions(+), 28 deletions(-) diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index f979def40..de5a7ef38 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -341,42 +341,18 @@ void PetComponent::Update(float deltaTime) { } if (m_Owner == LWOOBJID_EMPTY) { - if (m_Tamer != LWOOBJID_EMPTY) { - if (m_Timer > 0) { - m_Timer -= deltaTime; - - if (m_Timer <= 0) { - m_Timer = 0; - - ClientFailTamingMinigame(); - } - } - } else { - if (m_Timer > 0) { - m_Timer -= deltaTime; - - if (m_Timer <= 0) { - Wander(); - Game::entityManager->SerializeEntity(m_Parent); - } - } else { - m_Timer = 5; - } - } - + UpdateUnowned(deltaTime); return; } auto* owner = GetOwner(); - if (owner == nullptr) { + if (!owner) { m_Parent->Kill(); return; } m_MovementAI = m_Parent->GetComponent(); - if (m_MovementAI == nullptr) { - return; - } + if (!m_MovementAI) return; if (m_TresureTime > 0.0f) { //TODO: Find better trigger? InteractDig(deltaTime); @@ -404,7 +380,6 @@ void PetComponent::Update(float deltaTime) { if (m_Timer > 0) { m_Timer -= deltaTime; - return; } @@ -468,6 +443,29 @@ void PetComponent::Update(float deltaTime) { m_Timer = 1; } +void PetComponent::UpdateUnowned(float deltaTime) { + if (m_Tamer != LWOOBJID_EMPTY) { + if (m_Timer > 0) { + m_Timer -= deltaTime; + + if (m_Timer <= 0) { + m_Timer = 0; + + ClientFailTamingMinigame(); + } + } + } + else { + if (m_Timer > 0) { + m_Timer -= deltaTime; + + if (m_Timer <= 0) Wander(); + } else { + m_Timer = 5; + } + } +} + void PetComponent::SetIsReadyToDig(bool isReady) { if (isReady) { LOG("Dig state reached!"); diff --git a/dGame/dComponents/PetComponent.h b/dGame/dComponents/PetComponent.h index 4ec0ef29a..57f4a01fb 100644 --- a/dGame/dComponents/PetComponent.h +++ b/dGame/dComponents/PetComponent.h @@ -47,6 +47,12 @@ class PetComponent : public Component void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override; void Update(float deltaTime) override; + /** + * Handles updates for unowned pets + * @param deltaTime time since last update + */ + void UpdateUnowned(float deltaTime); + /** * Handles an OnUse event from another entity, initializing the pet taming minigame if this pet is untamed. * @param originator the entity that triggered the event From 1c01219ae9298c96c9c944b2ff558d88bda60131 Mon Sep 17 00:00:00 2001 From: jadebenn Date: Sat, 9 Dec 2023 22:24:35 -0600 Subject: [PATCH 21/73] added some pet debug commands --- dGame/dComponents/PetComponent.cpp | 8 +-- dGame/dComponents/PetComponent.h | 4 -- dGame/dUtilities/SlashCommandHandler.cpp | 86 ++++++++++++++++++++++++ 3 files changed, 90 insertions(+), 8 deletions(-) diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index de5a7ef38..d3dca79cd 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -155,12 +155,12 @@ void PetComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpd void PetComponent::OnUse(Entity* originator) { LOG("PET USE!"); - if(m_ReadyToDig) { + /*if(m_ReadyToDig) { LOG("Dig initiated!"); m_TresureTime = 2.0f; //m_ReadyToDig = false; SetAbility(PetAbilityType::DigAtPosition); - } + }*/ if (m_Owner != LWOOBJID_EMPTY) { return; @@ -434,9 +434,9 @@ void PetComponent::Update(float deltaTime) { skipTresure: - m_MovementAI->SetHaltDistance(haltDistance); + //m_MovementAI->SetHaltDistance(haltDistance); - m_MovementAI->SetMaxSpeed(2.5f); + //m_MovementAI->SetMaxSpeed(2.5f); m_MovementAI->SetDestination(destination); diff --git a/dGame/dComponents/PetComponent.h b/dGame/dComponents/PetComponent.h index 57f4a01fb..808df6a32 100644 --- a/dGame/dComponents/PetComponent.h +++ b/dGame/dComponents/PetComponent.h @@ -8,10 +8,6 @@ enum PetStatus : uint32_t { NONE, - UNKNOWN1 = 0x1, - UNKNOWN2 = 0x2, - UNKNOWN3 = 0x4, - UNKNOWN4 = 0x8, BEING_TAMED = 0x10, IS_NOT_WAITING = 0x20, // Right name? - used to be decimal 20 PLAY_SPAWN_ANIM = 0x80, diff --git a/dGame/dUtilities/SlashCommandHandler.cpp b/dGame/dUtilities/SlashCommandHandler.cpp index 9e27cc0fa..6b7fd0c20 100644 --- a/dGame/dUtilities/SlashCommandHandler.cpp +++ b/dGame/dUtilities/SlashCommandHandler.cpp @@ -803,6 +803,92 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit petComponent->Command(commandPos, entity->GetObjectID(), commandType, typeId, true); } + // Pet speed utility + if (chatCommand == "setpetmaxspeed" && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER) { + if (args.size() != 1) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid number of arguments!"); + return; + } + + float petMaxSpeed; + if (!GeneralUtils::TryParse(args[0], petMaxSpeed)) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid speed!"); + return; + } + + // Determine if player has a pet summoned + auto* petComponent = PetComponent::GetActivePet(entity->GetObjectID()); + if (!petComponent) { + ChatPackets::SendSystemMessage(sysAddr, u"No active pet found!"); + return; + } + + auto* movementAIComponent = petComponent->GetParentEntity()->GetComponent(); + if (!movementAIComponent) return; + + movementAIComponent->SetMaxSpeed(petMaxSpeed); + + std::u16string msg = u"Set pet max speed to " + (GeneralUtils::to_u16string(petMaxSpeed)); + ChatPackets::SendSystemMessage(sysAddr, msg); + } + + // Set pet acceleration + if (chatCommand == "setpetaccel" && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER) { + if (args.size() != 1) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid number of arguments!"); + return; + } + + float petAccel; + if (!GeneralUtils::TryParse(args[0], petAccel)) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid acceleration!"); + return; + } + + // Determine if player has a pet summoned + auto* petComponent = PetComponent::GetActivePet(entity->GetObjectID()); + if (!petComponent) { + ChatPackets::SendSystemMessage(sysAddr, u"No active pet found!"); + return; + } + + auto* movementAIComponent = petComponent->GetParentEntity()->GetComponent(); + if (!movementAIComponent) return; + + movementAIComponent->SetAcceleration(petAccel); + + std::u16string msg = u"Set pet acceleration to " + (GeneralUtils::to_u16string(petAccel)); + ChatPackets::SendSystemMessage(sysAddr, msg); + } + + // Set pet halt distance + if (chatCommand == "setpethalt" && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER) { + if (args.size() != 1) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid number of arguments!"); + return; + } + + float petHaltDistance; + if (!GeneralUtils::TryParse(args[0], petHaltDistance)) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid halt distance!"); + return; + } + + // Determine if player has a pet summoned + auto* petComponent = PetComponent::GetActivePet(entity->GetObjectID()); + if (!petComponent) { + ChatPackets::SendSystemMessage(sysAddr, u"No active pet found!"); + return; + } + + auto* movementAIComponent = petComponent->GetParentEntity()->GetComponent(); + if (!movementAIComponent) return; + + movementAIComponent->SetHaltDistance(petHaltDistance); + + std::u16string msg = u"Set pet halt distance to " + (GeneralUtils::to_u16string(petHaltDistance)); + ChatPackets::SendSystemMessage(sysAddr, msg); + } if (chatCommand == "playeffect" && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER && args.size() >= 3) { int32_t effectID = 0; From 200d679dd8dfbd1c3b57e40410f9354513284196 Mon Sep 17 00:00:00 2001 From: jadebenn Date: Sun, 10 Dec 2023 19:55:36 -0600 Subject: [PATCH 22/73] redone pet update loop --- dGame/dComponents/PetComponent.cpp | 222 +++++++++++++++++++++++------ dGame/dComponents/PetComponent.h | 52 ++++++- 2 files changed, 226 insertions(+), 48 deletions(-) diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index d3dca79cd..faaad97b2 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -82,32 +82,41 @@ PetComponent::PetComponent(Entity* parent, uint32_t componentId): Component(pare m_DatabaseId = LWOOBJID_EMPTY; m_Status = PetStatus::TAMEABLE; // Tameable m_Ability = PetAbilityType::Invalid; - m_StartPosition = NiPoint3::ZERO; + m_StartPosition = m_Parent->GetPosition(); //NiPoint3::ZERO; m_MovementAI = nullptr; m_TresureTime = 0; m_Preconditions = nullptr; m_ReadyToDig = false; - m_InInteract = false; + SetPetAiState(PetAiState::spawn); std::string checkPreconditions = GeneralUtils::UTF16ToWTF8(parent->GetVar(u"CheckPrecondition")); if (!checkPreconditions.empty()) { SetPreconditions(checkPreconditions); } - // Get the imagination drain rate from the CDClient - auto query = CDClientDatabase::CreatePreppedStmt("SELECT imaginationDrainRate FROM PetComponent WHERE id = ?;"); - + + // Get pet information from the CDClient + auto query = CDClientDatabase::CreatePreppedStmt( + "SELECT walkSpeed, runSpeed, sprintSpeed, imaginationDrainRate FROM PetComponent WHERE id = ?;"); query.bind(1, static_cast(componentId)); auto result = query.execQuery(); - // Should a result not exist for this pet default to 60 seconds. - if (!result.eof() && !result.fieldIsNull(0)) { - imaginationDrainRate = result.getFloatField(0, 60.0f); - } else { - imaginationDrainRate = 60.0f; + if (!result.eof()) { + if (!result.fieldIsNull(0)) + m_walkSpeed = result.getFloatField(0); + + if (!result.fieldIsNull(1)) + m_RunSpeed = result.getFloatField(1); + + if (!result.fieldIsNull(2)) + m_SprintSpeed = result.getFloatField(2); + + if (!result.fieldIsNull(3)) + imaginationDrainRate = result.getFloatField(3); } + result.finalize(); } @@ -152,19 +161,17 @@ void PetComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpd } } +void PetComponent::SetPetAiState(PetAiState newState) { + if (newState == GetPetAiState()) return; + this->m_State = newState; + LOG_DEBUG("Set pet AI state!"); + //Game::entityManager->SerializeEntity(m_Parent); // Do we need to serialize entity? +} + void PetComponent::OnUse(Entity* originator) { LOG("PET USE!"); - /*if(m_ReadyToDig) { - LOG("Dig initiated!"); - m_TresureTime = 2.0f; - //m_ReadyToDig = false; - SetAbility(PetAbilityType::DigAtPosition); - }*/ - - if (m_Owner != LWOOBJID_EMPTY) { - return; - } + if (m_Owner != LWOOBJID_EMPTY) return; if (m_Tamer != LWOOBJID_EMPTY) { auto* tamer = Game::entityManager->GetEntity(m_Tamer); @@ -336,29 +343,159 @@ void PetComponent::OnUse(Entity* originator) { } void PetComponent::Update(float deltaTime) { - if (m_StartPosition == NiPoint3::ZERO) { - m_StartPosition = m_Parent->GetPosition(); - } - + // If pet does not have an owner, use the UpdateUnowned() loop if (m_Owner == LWOOBJID_EMPTY) { UpdateUnowned(deltaTime); return; } + // Determine pet owner auto* owner = GetOwner(); if (!owner) { - m_Parent->Kill(); + m_Parent->Kill(); // Kill pet if no owner return; } - m_MovementAI = m_Parent->GetComponent(); - if (!m_MovementAI) return; + // Update timer + if (m_Timer > 0) { + m_Timer -= deltaTime; + return; + } + // Handle treasure timer if (m_TresureTime > 0.0f) { //TODO: Find better trigger? InteractDig(deltaTime); return; } + // Handle pet AI states + switch (m_State) { + // Handle idle state + case PetAiState::idle: { + LOG_DEBUG("Pet in idle state!"); + m_Timer = 1.0f; + break; + } + // Handle follow state + case PetAiState::follow: { + // Get movement AI component + m_MovementAI = m_Parent->GetComponent(); + if (!m_MovementAI) return; + + // Get and set destination + //auto position = m_MovementAI->GetParent()->GetPosition(); + auto ownerPos = owner->GetPosition(); + NiPoint3 destination = ownerPos; + NiPoint3 interactPos = NiPoint3::ZERO; + + // Determine if the "Lost Tags" mission has been completed and digging has been unlocked + auto* missionComponent = owner->GetComponent(); + if (!missionComponent) return; + const bool digUnlocked = missionComponent->GetMissionState(842) == eMissionState::COMPLETE; + + // Interactions checks + SwitchComponent* closestSwitch = SwitchComponent::GetClosestSwitch(ownerPos); + Entity* closestTreasure = PetDigServer::GetClosestTresure(ownerPos); + if (closestSwitch != nullptr && !closestSwitch->GetActive()) { + m_Interaction = closestSwitch->GetParentEntity()->GetObjectID(); + interactPos = closestSwitch->GetParentEntity()->GetPosition(); + m_Ability = PetAbilityType::GoToObject; + } + if (closestTreasure != nullptr && digUnlocked) { + m_Interaction = closestTreasure->GetObjectID(); + interactPos = closestTreasure->GetPosition(); + m_Ability = PetAbilityType::GoToObject; + } + + // Trigger interaction if checks are valid + if (m_Ability != PetAbilityType::Invalid) { + float distance = Vector3::DistanceSquared(ownerPos, interactPos); + if (distance < 20 * 20) { + destination = interactPos; + m_MovementAI->SetHaltDistance(0.0f); + SetPetAiState(PetAiState::interact); + } + } + + m_MovementAI->SetDestination(destination); + //LOG_DEBUG("Pet destination: %f %f %f", destination.x, destination.y, destination.z); + m_Timer = 1.0f; + + break; + } + // Handle interact state + case PetAiState::interact: { + LOG_DEBUG("Interacting with object!"); + + // Get movement AI component + m_MovementAI = m_Parent->GetComponent(); + if (!m_MovementAI) return; + + // Get distance from owner + auto ownerPos = owner->GetPosition(); + auto position = m_MovementAI->GetParent()->GetPosition(); + float distanceFromOwner = Vector3::DistanceSquared(position, ownerPos); + + // Switch back to follow AI state if player moves too far away from pet + if (distanceFromOwner > 15 * 15) { + m_MovementAI->SetHaltDistance(5.0f); // TODO: Remove this magic number + m_Interaction = LWOOBJID_EMPTY; + m_Ability = PetAbilityType::Invalid; + SetIsReadyToDig(false); + SetPetAiState(PetAiState::follow); + LOG_DEBUG("Pet interaction aborted due to player distance!"); + break; + } + + // Get distance from interactable + auto destination = m_MovementAI->GetDestination(); + float distanceFromInteract = Vector3::DistanceSquared(position, destination); + + // Handle the interaction + if (m_MovementAI->AtFinalWaypoint()) { + Entity* interactEntity = Game::entityManager->GetEntity(m_Interaction); + + /*auto* switchComponent = interactEntity->GetComponent(); + if (switchComponent != nullptr) { + switchComponent->EntityEnter(m_Parent); + } + else {*/ + Command(NiPoint3::ZERO, LWOOBJID_EMPTY, 1, PetEmote::Bounce, true); // Plays 'bounce' animation + SetIsReadyToDig(true); + //} + } + + m_Timer = 1.0f; + break; + } + // Handle spawn state + case PetAiState::spawn: { + LOG_DEBUG("Pet spawned!"); + + // Get movement AI component + m_MovementAI = m_Parent->GetComponent(); + if (!m_MovementAI) return; + + // Determine the pet start position + if (m_StartPosition == NiPoint3::ZERO) m_StartPosition = m_Parent->GetPosition(); + + // Determine next state; + if (m_Owner == LWOOBJID_EMPTY) { + SetPetAiState(PetAiState::idle); + } + else { + SetPetAiState(PetAiState::follow); + m_MovementAI->SetMaxSpeed(m_SprintSpeed); + m_MovementAI->SetHaltDistance(5.0f); // TODO: Remove magic number + } + break; + } + } + + return; + + /* + auto destination = owner->GetPosition(); NiPoint3 position = m_MovementAI->GetParent()->GetPosition(); @@ -387,18 +524,15 @@ void PetComponent::Update(float deltaTime) { float haltDistance = 5; - if (closestSwitch != nullptr) { - if (!closestSwitch->GetActive()) { - NiPoint3 switchPosition = closestSwitch->GetParentEntity()->GetPosition(); - float distance = Vector3::DistanceSquared(position, switchPosition); - if (distance < 3 * 3) { - m_Interaction = closestSwitch->GetParentEntity()->GetObjectID(); - closestSwitch->EntityEnter(m_Parent); - } else if (distance < 20 * 20) { - haltDistance = 1; - - destination = switchPosition; - } + if (closestSwitch != nullptr && !closestSwitch->GetActive()) { + NiPoint3 switchPosition = closestSwitch->GetParentEntity()->GetPosition(); + float distance = Vector3::DistanceSquared(position, switchPosition); + if (distance < 3 * 3) { + m_Interaction = closestSwitch->GetParentEntity()->GetObjectID(); + closestSwitch->EntityEnter(m_Parent); + } else if (distance < 20 * 20) { + haltDistance = 1; + destination = switchPosition; } } @@ -434,13 +568,13 @@ void PetComponent::Update(float deltaTime) { skipTresure: - //m_MovementAI->SetHaltDistance(haltDistance); + m_MovementAI->SetHaltDistance(haltDistance); //m_MovementAI->SetMaxSpeed(2.5f); m_MovementAI->SetDestination(destination); - m_Timer = 1; + m_Timer = 1;*/ } void PetComponent::UpdateUnowned(float deltaTime) { @@ -473,6 +607,7 @@ void PetComponent::SetIsReadyToDig(bool isReady) { //SetAbility(PetAbilityType::JumpOnObject); SetStatus(PetStatus::IS_NOT_WAITING); // Treasure dig status m_ReadyToDig = true; + Game::entityManager->SerializeEntity(m_Parent); } else { LOG("Dig state ended!"); @@ -480,6 +615,7 @@ void PetComponent::SetIsReadyToDig(bool isReady) { //SetAbility(PetAbilityType::Invalid); SetStatus(0); // TODO: Check status m_ReadyToDig = false; + Game::entityManager->SerializeEntity(m_Parent); } } @@ -1022,7 +1158,7 @@ void PetComponent::Command(NiPoint3 position, LWOOBJID source, int32_t commandTy // Emotes GameMessages::SendPlayEmote(m_Parent->GetObjectID(), typeId, owner->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS); } else if (commandType == 3) { - // Follow me, ??? + SetPetAiState(PetAiState::follow); } else if (commandType == 6) { // TODO: Go to player } @@ -1034,7 +1170,7 @@ void PetComponent::Command(NiPoint3 position, LWOOBJID source, int32_t commandTy // Add movement functionality if (position != NiPoint3::ZERO) { m_MovementAI->SetDestination(position); - m_Timer = 9; //Is this setting how long until the next update tick? + //m_Timer = 9; //Is this setting how long until the next update tick? } } diff --git a/dGame/dComponents/PetComponent.h b/dGame/dComponents/PetComponent.h index 808df6a32..d3b21f94f 100644 --- a/dGame/dComponents/PetComponent.h +++ b/dGame/dComponents/PetComponent.h @@ -6,6 +6,21 @@ #include "Preconditions.h" #include "eReplicaComponentType.h" +/* +* The current state of the pet AI +*/ +enum class PetAiState : uint { + idle = 0, // Doing nothing + spawn, // Spawning into the world + follow, // Following player + interact, // Beginning interaction + goToObj, // Go to object + despawn // Despawning from world +}; + +/* +* The status of the pet: Governs the icon above their head and the interactions available +*/ enum PetStatus : uint32_t { NONE, BEING_TAMED = 0x10, @@ -41,6 +56,18 @@ class PetComponent : public Component ~PetComponent() override; void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override; + + /** + * Sets the AI state of the pet + * @param newState New pet AI state + */ + void SetPetAiState(PetAiState newState); + + /** + * Gets the AI state of the pet + */ + PetAiState GetPetAiState() { return m_State; }; + void Update(float deltaTime) override; /** @@ -373,6 +400,11 @@ class PetComponent : public Component */ uint32_t m_Status; + /** + * The current state of the pet AI + */ + PetAiState m_State; + /** * A currently active ability, mostly unused */ @@ -399,11 +431,6 @@ class PetComponent : public Component */ bool m_ReadyToDig; - /** - * Boolean that sets if a pet is in an interaction - */ - bool m_InInteract; - /** * The position that this pet was spawned at */ @@ -423,4 +450,19 @@ class PetComponent : public Component * The rate at which imagination is drained from the user for having the pet out. */ float imaginationDrainRate; + + /** + * The walk speed of the pet + */ + float m_walkSpeed; + + /** + * The run speed of the pet + */ + float m_RunSpeed; + + /** + * The sprint speed of the pet + */ + float m_SprintSpeed; }; From 5b738dfc588d6be2e24e0065390c2b4a7f2a7fa0 Mon Sep 17 00:00:00 2001 From: jadebenn Date: Mon, 11 Dec 2023 21:10:29 -0600 Subject: [PATCH 23/73] Further redid pet update loop --- dGame/dComponents/BaseCombatAIComponent.cpp | 1 - dGame/dComponents/PetComponent.cpp | 340 ++++++++++---------- dGame/dComponents/PetComponent.h | 77 ++++- 3 files changed, 244 insertions(+), 174 deletions(-) diff --git a/dGame/dComponents/BaseCombatAIComponent.cpp b/dGame/dComponents/BaseCombatAIComponent.cpp index 1961e6d6c..b5b50b278 100644 --- a/dGame/dComponents/BaseCombatAIComponent.cpp +++ b/dGame/dComponents/BaseCombatAIComponent.cpp @@ -34,7 +34,6 @@ BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id): m_MovementAI = nullptr; m_Disabled = false; m_SkillEntries = {}; - m_MovementAI = nullptr; m_SoftTimer = 5.0f; //Grab the aggro information from BaseCombatAI: diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index faaad97b2..d74da1d54 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -73,6 +73,7 @@ PetComponent::PetComponent(Entity* parent, uint32_t componentId): Component(pare m_ComponentId = componentId; m_Interaction = LWOOBJID_EMPTY; + m_InteractType = PetInteractType::none; m_Owner = LWOOBJID_EMPTY; m_ModerationStatus = 0; m_Tamer = LWOOBJID_EMPTY; @@ -89,6 +90,7 @@ PetComponent::PetComponent(Entity* parent, uint32_t componentId): Component(pare m_ReadyToDig = false; SetPetAiState(PetAiState::spawn); + m_FollowRadius = 5.0f; std::string checkPreconditions = GeneralUtils::UTF16ToWTF8(parent->GetVar(u"CheckPrecondition")); @@ -104,17 +106,10 @@ PetComponent::PetComponent(Entity* parent, uint32_t componentId): Component(pare auto result = query.execQuery(); if (!result.eof()) { - if (!result.fieldIsNull(0)) - m_walkSpeed = result.getFloatField(0); - - if (!result.fieldIsNull(1)) - m_RunSpeed = result.getFloatField(1); - - if (!result.fieldIsNull(2)) - m_SprintSpeed = result.getFloatField(2); - - if (!result.fieldIsNull(3)) - imaginationDrainRate = result.getFloatField(3); + m_walkSpeed = result.getFloatField(0, 2.5f); + m_RunSpeed = result.getFloatField(1, 5.0f); + m_SprintSpeed = result.getFloatField(2, 10.0f); + imaginationDrainRate = result.getFloatField(3, 60.0f); } result.finalize(); @@ -176,22 +171,16 @@ void PetComponent::OnUse(Entity* originator) { if (m_Tamer != LWOOBJID_EMPTY) { auto* tamer = Game::entityManager->GetEntity(m_Tamer); - if (tamer != nullptr) { - return; - } + if (tamer != nullptr) return; m_Tamer = LWOOBJID_EMPTY; } auto* inventoryComponent = originator->GetComponent(); - if (inventoryComponent == nullptr) { - return; - } + if (inventoryComponent == nullptr) return; - if (m_Preconditions != nullptr && !m_Preconditions->Check(originator, true)) { - return; - } + if (m_Preconditions != nullptr && !m_Preconditions->Check(originator, true)) return; auto* movementAIComponent = m_Parent->GetComponent(); @@ -246,15 +235,11 @@ void PetComponent::OnUse(Entity* originator) { auto* destroyableComponent = originator->GetComponent(); - if (destroyableComponent == nullptr) { - return; - } + if (destroyableComponent == nullptr) return; auto imagination = destroyableComponent->GetImagination(); - if (imagination < imaginationCost) { - return; - } + if (imagination < imaginationCost) return; const auto& bricks = BrickDatabase::GetBricks(buildFile); @@ -344,7 +329,7 @@ void PetComponent::OnUse(Entity* originator) { void PetComponent::Update(float deltaTime) { // If pet does not have an owner, use the UpdateUnowned() loop - if (m_Owner == LWOOBJID_EMPTY) { + /*if (m_Owner == LWOOBJID_EMPTY) { UpdateUnowned(deltaTime); return; } @@ -354,6 +339,10 @@ void PetComponent::Update(float deltaTime) { if (!owner) { m_Parent->Kill(); // Kill pet if no owner return; + }*/ + + if (m_StartPosition == NiPoint3::ZERO) { + m_StartPosition = m_Parent->GetPosition(); } // Update timer @@ -362,138 +351,40 @@ void PetComponent::Update(float deltaTime) { return; } - // Handle treasure timer - if (m_TresureTime > 0.0f) { //TODO: Find better trigger? - InteractDig(deltaTime); - return; - } - - // Handle pet AI states switch (m_State) { - // Handle idle state - case PetAiState::idle: { - LOG_DEBUG("Pet in idle state!"); - m_Timer = 1.0f; - break; + case PetAiState::spawn: + LOG_DEBUG("Pet spawn beginning!"); + OnSpawn(); + break; + + case PetAiState::idle: + Wander(); + break; + + case PetAiState::follow: + OnFollow(); + break; + + case PetAiState::goToObj: + LOG_DEBUG("Going to object!"); + if (m_MovementAI->AtFinalWaypoint()) { + LOG_DEBUG("Reached object!"); + m_MovementAI->Stop(); + SetPetAiState(PetAiState::interact); } - // Handle follow state - case PetAiState::follow: { - // Get movement AI component - m_MovementAI = m_Parent->GetComponent(); - if (!m_MovementAI) return; - - // Get and set destination - //auto position = m_MovementAI->GetParent()->GetPosition(); - auto ownerPos = owner->GetPosition(); - NiPoint3 destination = ownerPos; - NiPoint3 interactPos = NiPoint3::ZERO; - - // Determine if the "Lost Tags" mission has been completed and digging has been unlocked - auto* missionComponent = owner->GetComponent(); - if (!missionComponent) return; - const bool digUnlocked = missionComponent->GetMissionState(842) == eMissionState::COMPLETE; - - // Interactions checks - SwitchComponent* closestSwitch = SwitchComponent::GetClosestSwitch(ownerPos); - Entity* closestTreasure = PetDigServer::GetClosestTresure(ownerPos); - if (closestSwitch != nullptr && !closestSwitch->GetActive()) { - m_Interaction = closestSwitch->GetParentEntity()->GetObjectID(); - interactPos = closestSwitch->GetParentEntity()->GetPosition(); - m_Ability = PetAbilityType::GoToObject; - } - if (closestTreasure != nullptr && digUnlocked) { - m_Interaction = closestTreasure->GetObjectID(); - interactPos = closestTreasure->GetPosition(); - m_Ability = PetAbilityType::GoToObject; - } - - // Trigger interaction if checks are valid - if (m_Ability != PetAbilityType::Invalid) { - float distance = Vector3::DistanceSquared(ownerPos, interactPos); - if (distance < 20 * 20) { - destination = interactPos; - m_MovementAI->SetHaltDistance(0.0f); - SetPetAiState(PetAiState::interact); - } - } - - m_MovementAI->SetDestination(destination); - //LOG_DEBUG("Pet destination: %f %f %f", destination.x, destination.y, destination.z); - m_Timer = 1.0f; - - break; - } - // Handle interact state - case PetAiState::interact: { - LOG_DEBUG("Interacting with object!"); - - // Get movement AI component - m_MovementAI = m_Parent->GetComponent(); - if (!m_MovementAI) return; - - // Get distance from owner - auto ownerPos = owner->GetPosition(); - auto position = m_MovementAI->GetParent()->GetPosition(); - float distanceFromOwner = Vector3::DistanceSquared(position, ownerPos); - - // Switch back to follow AI state if player moves too far away from pet - if (distanceFromOwner > 15 * 15) { - m_MovementAI->SetHaltDistance(5.0f); // TODO: Remove this magic number - m_Interaction = LWOOBJID_EMPTY; - m_Ability = PetAbilityType::Invalid; - SetIsReadyToDig(false); - SetPetAiState(PetAiState::follow); - LOG_DEBUG("Pet interaction aborted due to player distance!"); - break; - } - - // Get distance from interactable - auto destination = m_MovementAI->GetDestination(); - float distanceFromInteract = Vector3::DistanceSquared(position, destination); - - // Handle the interaction - if (m_MovementAI->AtFinalWaypoint()) { - Entity* interactEntity = Game::entityManager->GetEntity(m_Interaction); - - /*auto* switchComponent = interactEntity->GetComponent(); - if (switchComponent != nullptr) { - switchComponent->EntityEnter(m_Parent); - } - else {*/ - Command(NiPoint3::ZERO, LWOOBJID_EMPTY, 1, PetEmote::Bounce, true); // Plays 'bounce' animation - SetIsReadyToDig(true); - //} - } - - m_Timer = 1.0f; - break; + else { + m_Timer += 0.5f; } - // Handle spawn state - case PetAiState::spawn: { - LOG_DEBUG("Pet spawned!"); + break; - // Get movement AI component - m_MovementAI = m_Parent->GetComponent(); - if (!m_MovementAI) return; + case PetAiState::interact: + OnInteract(); + break; - // Determine the pet start position - if (m_StartPosition == NiPoint3::ZERO) m_StartPosition = m_Parent->GetPosition(); - - // Determine next state; - if (m_Owner == LWOOBJID_EMPTY) { - SetPetAiState(PetAiState::idle); - } - else { - SetPetAiState(PetAiState::follow); - m_MovementAI->SetMaxSpeed(m_SprintSpeed); - m_MovementAI->SetHaltDistance(5.0f); // TODO: Remove magic number - } - break; - } + default: + break; } - return; - /* auto destination = owner->GetPosition(); @@ -577,7 +468,7 @@ void PetComponent::Update(float deltaTime) { m_Timer = 1;*/ } -void PetComponent::UpdateUnowned(float deltaTime) { +void PetComponent::UpdateUnowned(float deltaTime) { //CURRENTLY UNUSED if (m_Tamer != LWOOBJID_EMPTY) { if (m_Timer > 0) { m_Timer -= deltaTime; @@ -967,9 +858,7 @@ void PetComponent::ClientFailTamingMinigame() { void PetComponent::Wander() { m_MovementAI = m_Parent->GetComponent(); - if (m_MovementAI == nullptr || !m_MovementAI->AtFinalWaypoint()) { - return; - } + if (m_MovementAI == nullptr || !m_MovementAI->AtFinalWaypoint()) return; m_MovementAI->SetHaltDistance(0); @@ -1007,6 +896,135 @@ void PetComponent::Wander() { m_Timer += (m_MovementAI->GetParent()->GetPosition().x - destination.x) / info.wanderSpeed; } +void PetComponent::OnSpawn() { + m_MovementAI = m_Parent->GetComponent(); + if (!m_MovementAI) return; + + LOG_DEBUG("Pet spawn complete, setting AI state."); + + if (m_Owner != LWOOBJID_EMPTY) SetPetAiState(PetAiState::follow); + else SetPetAiState(PetAiState::idle); +} + +void PetComponent::OnFollow() { + m_MovementAI->SetHaltDistance(5.0f); // TODO: Remove this magic number + + Entity* owner = GetOwner(); + NiPoint3 ownerPos = owner->GetPosition(); + NiPoint3 currentPos = m_MovementAI->GetParent()->GetPosition(); + + // If the player's position is within range, stop moving + if (Vector3::DistanceSquared(currentPos, ownerPos) <= m_FollowRadius * m_FollowRadius) { + m_MovementAI->Stop(); + } + else { // Chase the player's new position + m_MovementAI->SetMaxSpeed(m_SprintSpeed); + m_MovementAI->SetDestination(ownerPos); + LOG_DEBUG("New pet destination: %f %f %f", ownerPos.x, ownerPos.y, ownerPos.z); + //SetPetAiState(PetAiState::tether); + } + + //TEST SECTION + SwitchComponent* closestSwitch = SwitchComponent::GetClosestSwitch(ownerPos); + if (closestSwitch != nullptr && !closestSwitch->GetActive()) { + NiPoint3 switchPos = closestSwitch->GetParentEntity()->GetPosition(); + const float distance = Vector3::DistanceSquared(ownerPos, switchPos); + if (distance < 20 * 20) { + StartInteract(switchPos, PetInteractType::bouncer); + return; + } + } + + // Determine if the "Lost Tags" mission has been completed and digging has been unlocked + auto* missionComponent = owner->GetComponent(); + if (!missionComponent) return; + const bool digUnlocked = missionComponent->GetMissionState(842) == eMissionState::COMPLETE; + + Entity* closestTreasure = PetDigServer::GetClosestTresure(ownerPos); + if (closestTreasure != nullptr && digUnlocked) { + NiPoint3 treasurePos = closestTreasure->GetPosition(); + const float distance = Vector3::DistanceSquared(ownerPos, treasurePos); + if (distance < 10 * 10) { + StartInteract(treasurePos, PetInteractType::treasure); + return; + } + } + + m_Timer += 0.5f; +} + +void PetComponent::OnInteract() { + LOG_DEBUG("Beginning interaction with object!"); + + NiPoint3 ownerPos = GetOwner()->GetPosition(); + NiPoint3 currentPos = m_MovementAI->GetParent()->GetPosition(); + const float distanceFromOwner = Vector3::DistanceSquared(ownerPos, currentPos); + + if (distanceFromOwner > 25 * 25) { + LOG_DEBUG("Disengaging from object interaction due to player distance."); + StopInteract(); + return; + } + + switch (GetInteractType()) { + case PetInteractType::bouncer: + StartInteractBouncer(); + break; + + case PetInteractType::treasure: + StartInteractDig(); + break; + + default: + LOG_DEBUG("INTERACT = NONE! RETURNING!"); + StopInteract(); + m_Timer += 0.5f; + break; + } +} + +void PetComponent::OnTether() { + m_Timer += 0.5f; +} + +void PetComponent::StartInteract(NiPoint3 position, PetInteractType interactType) { + SetInteractType(interactType); + SetAbility(PetAbilityType::GoToObject); + SetPetAiState(PetAiState::goToObj); + m_MovementAI->SetMaxSpeed(m_RunSpeed); + m_MovementAI->SetHaltDistance(0.0f); + m_MovementAI->SetDestination(position); + LOG_DEBUG("Starting interaction!"); + Game::entityManager->SerializeEntity(m_Parent); +} + +void PetComponent::StopInteract() { + SetInteractType(PetInteractType::none); + SetAbility(PetAbilityType::Invalid); + SetPetAiState(PetAiState::follow); + m_MovementAI->SetMaxSpeed(m_SprintSpeed); + m_MovementAI->SetHaltDistance(m_FollowRadius); + LOG_DEBUG("Stopping interaction!"); + Game::entityManager->SerializeEntity(m_Parent); +} + +void PetComponent::StartInteractBouncer() { + SetAbility(PetAbilityType::JumpOnObject); + NiPoint3 destination = m_MovementAI->GetDestination(); + SwitchComponent* closestSwitch = SwitchComponent::GetClosestSwitch(destination); + m_Interaction = closestSwitch->GetParentEntity()->GetObjectID(); + Game::entityManager->SerializeEntity(m_Parent); + closestSwitch->EntityEnter(m_Parent); +} + +void PetComponent::StartInteractDig() { + //m_InInteract = true; + //m_TresureTime = 2.0f; //TODO: Remove magic number + m_Interaction == LWOOBJID_EMPTY; //TODO: Make this not empty + Command(NiPoint3::ZERO, LWOOBJID_EMPTY, 1, PetEmote::Bounce, true);\ + m_Timer = 2.0f; +} + void PetComponent::Activate(Item* item, bool registerPet, bool fromTaming) { AddDrainImaginationTimer(item, fromTaming); @@ -1282,13 +1300,3 @@ void PetComponent::LoadPetNameFromModeration() { void PetComponent::SetPreconditions(std::string& preconditions) { m_Preconditions = new PreconditionExpression(preconditions); } - -void PetComponent::StartInteractDig() { - //m_InInteract = true; - m_TresureTime = 2.0f; //TODO: Remove magic number - Command(NiPoint3::ZERO, LWOOBJID_EMPTY, 1, PetEmote::DigTreasure, true); -} - -void PetComponent::EndInteractDig() { - //m_InInteract = false; -} \ No newline at end of file diff --git a/dGame/dComponents/PetComponent.h b/dGame/dComponents/PetComponent.h index d3b21f94f..58bc6386d 100644 --- a/dGame/dComponents/PetComponent.h +++ b/dGame/dComponents/PetComponent.h @@ -9,22 +9,31 @@ /* * The current state of the pet AI */ -enum class PetAiState : uint { +enum class PetAiState : uint8_t { idle = 0, // Doing nothing spawn, // Spawning into the world - follow, // Following player - interact, // Beginning interaction + follow, // Begin following goToObj, // Go to object + interact, // Interact with an object despawn // Despawning from world }; +/* +* The type of object the pet is interacting with +*/ +enum class PetInteractType : uint8_t { + none, // Not interacting + treasure, // Treasure dig + bouncer // Bouncer switch +}; + /* * The status of the pet: Governs the icon above their head and the interactions available */ enum PetStatus : uint32_t { NONE, BEING_TAMED = 0x10, - IS_NOT_WAITING = 0x20, // Right name? - used to be decimal 20 + IS_NOT_WAITING = 0x20, PLAY_SPAWN_ANIM = 0x80, TAMEABLE = 0x4000000 }; @@ -35,8 +44,7 @@ enum PetEmote : int32_t { Bounce }; -enum class PetAbilityType -{ +enum class PetAbilityType { Invalid, GoToObject, JumpOnObject, @@ -125,6 +133,46 @@ class PetComponent : public Component */ void Wander(); + /** + * Called when the pet is first spawned + */ + void OnSpawn(); + + /** + * Continues a step in the follow state, making sure that the entity is around its start position + */ + void OnFollow(); + + /** + * Continues a step in the interact state, handling the pet's interaction with an entity + */ + void OnInteract(); + + /** + * Continues a step in the tether state, making the entity run towards its target + */ + void OnTether(); + + /** + * Start a pet interaction with an object at a given position + */ + void StartInteract(NiPoint3 position, PetInteractType interactType); + + /** + * Stop a pet interaction with an object + */ + void StopInteract(); + + /** + * Set the type of interaction the pet is executing + */ + void SetInteractType(PetInteractType interactType) { m_InteractType = interactType; }; + + /** + * Get the type of interaction the pet is executing + */ + PetInteractType GetInteractType() { return m_InteractType; }; + /** * Spawns a pet from an item in the inventory of an owner * @param item the item to create the pet from @@ -231,7 +279,12 @@ class PetComponent : public Component bool GetIsReadyToDig() { return m_ReadyToDig; }; /** - * Start the dig interaction + * Start the pet bouncer interaction + */ + void StartInteractBouncer(); + + /** + * Start the treasure dig interaction */ void StartInteractDig(); @@ -360,6 +413,11 @@ class PetComponent : public Component */ LWOOBJID m_Interaction; + /** + * The type of object that the pet is currently interacting with (e.g. a treasure chest or switch) + */ + PetInteractType m_InteractType; + /** * The ID of the entity that owns this pet */ @@ -436,6 +494,11 @@ class PetComponent : public Component */ NiPoint3 m_StartPosition; + /** + * The halting radius of the pet while following a player + */ + float m_FollowRadius; + /** * The movement AI component that is related to this pet, required to move it around */ From a99f7a7fc1d1c4e6a489e8d060f59b0fcda8c63f Mon Sep 17 00:00:00 2001 From: jadebenn Date: Wed, 13 Dec 2023 00:14:53 -0600 Subject: [PATCH 24/73] Re-implemented treasure dig handling --- dGame/dComponents/PetComponent.cpp | 206 ++++++++++-------- dGame/dComponents/PetComponent.h | 58 ++--- .../02_server/Map/General/PetDigServer.cpp | 4 +- 3 files changed, 144 insertions(+), 124 deletions(-) diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index d74da1d54..a5d75de53 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -80,17 +80,17 @@ PetComponent::PetComponent(Entity* parent, uint32_t componentId): Component(pare m_ModelId = LWOOBJID_EMPTY; m_Timer = 0; m_TimerAway = 0; + m_TimerBounce = 0; m_DatabaseId = LWOOBJID_EMPTY; m_Status = PetStatus::TAMEABLE; // Tameable m_Ability = PetAbilityType::Invalid; m_StartPosition = m_Parent->GetPosition(); //NiPoint3::ZERO; m_MovementAI = nullptr; - m_TresureTime = 0; m_Preconditions = nullptr; - m_ReadyToDig = false; + m_ReadyToInteract = false; SetPetAiState(PetAiState::spawn); - m_FollowRadius = 5.0f; + //m_FollowRadius = 8.0f; std::string checkPreconditions = GeneralUtils::UTF16ToWTF8(parent->GetVar(u"CheckPrecondition")); @@ -160,7 +160,6 @@ void PetComponent::SetPetAiState(PetAiState newState) { if (newState == GetPetAiState()) return; this->m_State = newState; LOG_DEBUG("Set pet AI state!"); - //Game::entityManager->SerializeEntity(m_Parent); // Do we need to serialize entity? } void PetComponent::OnUse(Entity* originator) { @@ -341,16 +340,24 @@ void PetComponent::Update(float deltaTime) { return; }*/ - if (m_StartPosition == NiPoint3::ZERO) { - m_StartPosition = m_Parent->GetPosition(); - } + // Update timers + m_TimerBounce -= deltaTime; - // Update timer if (m_Timer > 0) { m_Timer -= deltaTime; return; } + // Remove "left behind" pets + if (m_Owner != LWOOBJID_EMPTY) { + Entity* owner = GetOwner(); + if (!owner) { + m_Parent->Kill(); + return; + } + } + + // Handle pet AI states switch (m_State) { case PetAiState::spawn: LOG_DEBUG("Pet spawn beginning!"); @@ -468,7 +475,7 @@ void PetComponent::Update(float deltaTime) { m_Timer = 1;*/ } -void PetComponent::UpdateUnowned(float deltaTime) { //CURRENTLY UNUSED +void PetComponent::UpdateUnowned(float deltaTime) { //TODO: CURRENTLY UNUSED if (m_Tamer != LWOOBJID_EMPTY) { if (m_Timer > 0) { m_Timer -= deltaTime; @@ -491,7 +498,7 @@ void PetComponent::UpdateUnowned(float deltaTime) { //CURRENTLY UNUSED } } -void PetComponent::SetIsReadyToDig(bool isReady) { +/*void PetComponent::SetIsReadyToDig(bool isReady) { if (isReady) { LOG("Dig state reached!"); //m_Interaction = closestTresure->GetObjectID(); @@ -508,33 +515,7 @@ void PetComponent::SetIsReadyToDig(bool isReady) { m_ReadyToDig = false; Game::entityManager->SerializeEntity(m_Parent); } -} - -void PetComponent::InteractDig(float deltaTime) { - LOG("Pet digging!"); - - auto* tresure = Game::entityManager->GetEntity(m_Interaction); - - if (tresure == nullptr) { - m_TresureTime = 0.0f; - return; - } - - m_TresureTime -= deltaTime; - - m_MovementAI->Stop(); - - if (m_TresureTime <= 0.0f) { - m_Parent->SetOwnerOverride(m_Owner); - - tresure->Smash(m_Parent->GetObjectID()); - - LOG("Pet dig completed!"); - m_Interaction = LWOOBJID_EMPTY; - m_TresureTime = 0.0f; - SetIsReadyToDig(false); - } -} +}*/ void PetComponent::TryBuild(uint32_t numBricks, bool clientFailed) { if (m_Tamer == LWOOBJID_EMPTY) return; @@ -856,9 +837,9 @@ void PetComponent::ClientFailTamingMinigame() { } void PetComponent::Wander() { - m_MovementAI = m_Parent->GetComponent(); + //m_MovementAI = m_Parent->GetComponent(); - if (m_MovementAI == nullptr || !m_MovementAI->AtFinalWaypoint()) return; + if (/*m_MovementAI == nullptr ||*/ !m_MovementAI->AtFinalWaypoint()) return; m_MovementAI->SetHaltDistance(0); @@ -889,48 +870,48 @@ void PetComponent::Wander() { return; } - m_MovementAI->SetMaxSpeed(info.wanderSpeed); + m_MovementAI->SetMaxSpeed(m_SprintSpeed); //info.wanderSpeed); m_MovementAI->SetDestination(destination); - m_Timer += (m_MovementAI->GetParent()->GetPosition().x - destination.x) / info.wanderSpeed; + m_Timer += (m_MovementAI->GetParent()->GetPosition().x - destination.x) / m_SprintSpeed; //info.wanderSpeed; } void PetComponent::OnSpawn() { m_MovementAI = m_Parent->GetComponent(); - if (!m_MovementAI) return; + //if (!m_MovementAI) return; + + if (m_StartPosition == NiPoint3::ZERO) { + m_StartPosition = m_Parent->GetPosition(); + } LOG_DEBUG("Pet spawn complete, setting AI state."); - if (m_Owner != LWOOBJID_EMPTY) SetPetAiState(PetAiState::follow); - else SetPetAiState(PetAiState::idle); + if (m_Owner != LWOOBJID_EMPTY) { + m_Parent->SetOwnerOverride(m_Owner); + m_MovementAI->SetMaxSpeed(m_SprintSpeed); + m_MovementAI->SetHaltDistance(m_FollowRadius); + SetStatus(PetStatus::NONE); + SetPetAiState(PetAiState::follow); + } + else { + SetPetAiState(PetAiState::idle); + } } void PetComponent::OnFollow() { - m_MovementAI->SetHaltDistance(5.0f); // TODO: Remove this magic number - Entity* owner = GetOwner(); - NiPoint3 ownerPos = owner->GetPosition(); - NiPoint3 currentPos = m_MovementAI->GetParent()->GetPosition(); - - // If the player's position is within range, stop moving - if (Vector3::DistanceSquared(currentPos, ownerPos) <= m_FollowRadius * m_FollowRadius) { - m_MovementAI->Stop(); - } - else { // Chase the player's new position - m_MovementAI->SetMaxSpeed(m_SprintSpeed); - m_MovementAI->SetDestination(ownerPos); - LOG_DEBUG("New pet destination: %f %f %f", ownerPos.x, ownerPos.y, ownerPos.z); - //SetPetAiState(PetAiState::tether); - } + if (!owner) return; + const NiPoint3 ownerPos = owner->GetPosition(); - //TEST SECTION + // Find interactions SwitchComponent* closestSwitch = SwitchComponent::GetClosestSwitch(ownerPos); if (closestSwitch != nullptr && !closestSwitch->GetActive()) { - NiPoint3 switchPos = closestSwitch->GetParentEntity()->GetPosition(); + const NiPoint3 switchPos = closestSwitch->GetParentEntity()->GetPosition(); + const LWOOBJID switchID = closestSwitch->GetParentEntity()->GetObjectID(); const float distance = Vector3::DistanceSquared(ownerPos, switchPos); - if (distance < 20 * 20) { - StartInteract(switchPos, PetInteractType::bouncer); + if (distance < 16 * 16) { + StartInteract(switchPos, PetInteractType::bouncer, switchID); return; } } @@ -942,22 +923,36 @@ void PetComponent::OnFollow() { Entity* closestTreasure = PetDigServer::GetClosestTresure(ownerPos); if (closestTreasure != nullptr && digUnlocked) { - NiPoint3 treasurePos = closestTreasure->GetPosition(); + const NiPoint3 treasurePos = closestTreasure->GetPosition(); + const LWOOBJID treasureID = closestTreasure->GetObjectID(); const float distance = Vector3::DistanceSquared(ownerPos, treasurePos); - if (distance < 10 * 10) { - StartInteract(treasurePos, PetInteractType::treasure); + if (distance < 16 * 16) { + StartInteract(treasurePos, PetInteractType::treasure, treasureID); return; } } + // Handle actual following logic + const NiPoint3 currentPos = m_MovementAI->GetParent()->GetPosition(); + + // If the player's position is within range, stop moving + if (Vector3::DistanceSquared(currentPos, ownerPos) <= m_FollowRadius * m_FollowRadius) { + m_MovementAI->Stop(); + } + else { // Chase the player's new position + m_MovementAI->SetDestination(ownerPos); + LOG_DEBUG("New pet destination: %f %f %f", ownerPos.x, ownerPos.y, ownerPos.z); + } + m_Timer += 0.5f; } void PetComponent::OnInteract() { - LOG_DEBUG("Beginning interaction with object!"); + Entity* owner = GetOwner(); + if (!owner) return; - NiPoint3 ownerPos = GetOwner()->GetPosition(); - NiPoint3 currentPos = m_MovementAI->GetParent()->GetPosition(); + const NiPoint3 ownerPos = owner->GetPosition(); + const NiPoint3 currentPos = m_MovementAI->GetParent()->GetPosition(); const float distanceFromOwner = Vector3::DistanceSquared(ownerPos, currentPos); if (distanceFromOwner > 25 * 25) { @@ -968,11 +963,13 @@ void PetComponent::OnInteract() { switch (GetInteractType()) { case PetInteractType::bouncer: - StartInteractBouncer(); + if (GetIsReadyToInteract()) LOG_DEBUG("Add the HandleInteractBouncer()!"); + else SetupInteractBouncer(); break; case PetInteractType::treasure: - StartInteractDig(); + if (GetIsReadyToInteract()) HandleInteractTreasureDig(); + else SetupInteractTreasureDig(); break; default: @@ -983,11 +980,8 @@ void PetComponent::OnInteract() { } } -void PetComponent::OnTether() { - m_Timer += 0.5f; -} - -void PetComponent::StartInteract(NiPoint3 position, PetInteractType interactType) { +void PetComponent::StartInteract(const NiPoint3 position, const PetInteractType interactType, const LWOOBJID interactID) { + SetInteraction(interactID); // TODO: Check if this should be serialized for goToObj SetInteractType(interactType); SetAbility(PetAbilityType::GoToObject); SetPetAiState(PetAiState::goToObj); @@ -999,30 +993,68 @@ void PetComponent::StartInteract(NiPoint3 position, PetInteractType interactType } void PetComponent::StopInteract() { + SetInteraction(LWOOBJID_EMPTY); SetInteractType(PetInteractType::none); SetAbility(PetAbilityType::Invalid); SetPetAiState(PetAiState::follow); + SetStatus(PetStatus::NONE); + SetIsReadyToInteract(false); m_MovementAI->SetMaxSpeed(m_SprintSpeed); m_MovementAI->SetHaltDistance(m_FollowRadius); LOG_DEBUG("Stopping interaction!"); Game::entityManager->SerializeEntity(m_Parent); } -void PetComponent::StartInteractBouncer() { - SetAbility(PetAbilityType::JumpOnObject); +void PetComponent::SetupInteractBouncer() { + // THIS IS ALL BAD, BAD, BAD! FIX IT, ME! >:( + /*SetAbility(PetAbilityType::JumpOnObject); NiPoint3 destination = m_MovementAI->GetDestination(); SwitchComponent* closestSwitch = SwitchComponent::GetClosestSwitch(destination); m_Interaction = closestSwitch->GetParentEntity()->GetObjectID(); Game::entityManager->SerializeEntity(m_Parent); - closestSwitch->EntityEnter(m_Parent); + closestSwitch->EntityEnter(m_Parent);*/ +} + +void PetComponent::SetupInteractTreasureDig() { + LOG_DEBUG("Setting up dig interaction!"); + Entity* closestTreasure = Game::entityManager->GetEntity(GetInteraction()); + if (!closestTreasure) return; + + SetIsReadyToInteract(true); + + SetStatus(PetStatus::IS_NOT_WAITING); // TODO: Double-check this is the right flag being set + Game::entityManager->SerializeEntity(m_Parent); // TODO: Double-check pet packet captures + m_Timer += 0.5f; } -void PetComponent::StartInteractDig() { - //m_InInteract = true; - //m_TresureTime = 2.0f; //TODO: Remove magic number - m_Interaction == LWOOBJID_EMPTY; //TODO: Make this not empty - Command(NiPoint3::ZERO, LWOOBJID_EMPTY, 1, PetEmote::Bounce, true);\ - m_Timer = 2.0f; +void PetComponent::StartInteractTreasureDig() { + SetAbility(PetAbilityType::DigAtPosition); + Game::entityManager->SerializeEntity(m_Parent); + + Command(NiPoint3::ZERO, LWOOBJID_EMPTY, 1, PetEmote::DigTreasure, true); // Plays 'dig' animation + m_Timer = 1.5f; +} + +void PetComponent::HandleInteractTreasureDig() { + if (GetAbility() == PetAbilityType::DigAtPosition) { + auto* owner = GetOwner(); + + auto* treasure = Game::entityManager->GetEntity(GetInteraction()); + if (!treasure) return; + treasure->Smash(m_Parent->GetObjectID()); + + LOG_DEBUG("Pet dig completed!"); + StopInteract(); //TODO: This may not be totally consistent with live behavior, where the pet seems to stay near the dig and not immediately follow + m_Timer = 1.5f; + return; + } + + if (m_TimerBounce <= 0.0f) { + Command(NiPoint3::ZERO, LWOOBJID_EMPTY, 1, PetEmote::Bounce, true); // Plays 'bounce' animation + m_TimerBounce = 1.0f; + } + + m_Timer += 0.5f; } void PetComponent::Activate(Item* item, bool registerPet, bool fromTaming) { @@ -1042,7 +1074,7 @@ void PetComponent::Activate(Item* item, bool registerPet, bool fromTaming) { auto* owner = GetOwner(); if (owner == nullptr) return; - SetStatus(1); + SetStatus(PetStatus::PLAY_SPAWN_ANIM); auto databaseData = inventoryComponent->GetDatabasePet(m_DatabaseId); @@ -1075,7 +1107,7 @@ void PetComponent::Activate(Item* item, bool registerPet, bool fromTaming) { activePets[m_Owner] = m_Parent->GetObjectID(); - m_Timer = 3; + //m_Timer = 3; Game::entityManager->SerializeEntity(m_Parent); diff --git a/dGame/dComponents/PetComponent.h b/dGame/dComponents/PetComponent.h index 58bc6386d..687da810f 100644 --- a/dGame/dComponents/PetComponent.h +++ b/dGame/dComponents/PetComponent.h @@ -32,6 +32,7 @@ enum class PetInteractType : uint8_t { */ enum PetStatus : uint32_t { NONE, + //READY_TO_DIG, BEING_TAMED = 0x10, IS_NOT_WAITING = 0x20, PLAY_SPAWN_ANIM = 0x80, @@ -148,15 +149,10 @@ class PetComponent : public Component */ void OnInteract(); - /** - * Continues a step in the tether state, making the entity run towards its target - */ - void OnTether(); - /** * Start a pet interaction with an object at a given position */ - void StartInteract(NiPoint3 position, PetInteractType interactType); + void StartInteract(const NiPoint3 position, const PetInteractType interactType, const LWOOBJID interactID); /** * Stop a pet interaction with an object @@ -268,42 +264,35 @@ class PetComponent : public Component void SetPreconditions(std::string& conditions); /** - * Sets if the pet is ready to dig - * @param isReady whether the pet is ready to dig (true) or not (false) + * Sets if the pet is ready to interact with an object + * @param isReady whether the pet is ready to interact (true) or not (false) */ - void SetIsReadyToDig(bool isReady); + void SetIsReadyToInteract(bool isReady) { m_ReadyToInteract = isReady; }; /** - * @return is pet ready to dig + * @return is pet ready to interact with an object */ - bool GetIsReadyToDig() { return m_ReadyToDig; }; + bool GetIsReadyToInteract() { return m_ReadyToInteract; }; /** - * Start the pet bouncer interaction + * Set up the pet bouncer interaction */ - void StartInteractBouncer(); - - /** - * Start the treasure dig interaction - */ - void StartInteractDig(); + void SetupInteractBouncer(); /** - * Handles the pet dig interaction - * @param deltaTime time elapsed + * Set up the treasure dig interaction */ - void InteractDig(float deltaTime); + void SetupInteractTreasureDig(); /** - * End the dig interaction - */ - void EndInteractDig(); + * Starts the pet treasure dig interaction + */ + void StartInteractTreasureDig(); /** - * Sets pet's treasure timer - * @param digTime float representing the treasure dig time in seconds - */ - void SetTreasureTime(float digTime) { m_TresureTime = digTime; }; + * Handles the pet treasure dig interaction + */ + void HandleInteractTreasureDig(); /** * Returns the entity that this component belongs to @@ -479,15 +468,14 @@ class PetComponent : public Component float m_TimerAway; /** - * Timer that tracks how long a pet has been digging up some treasure, required to spawn the treasure contents - * on time - */ - float m_TresureTime; + * A timer that tracks how long until a tamed pet will bounce again when standing over a treasure dig site + */ + float m_TimerBounce; /** - * Boolean that sets if a pet is ready to dig and display the interact prompt + * Boolean that sets if a pet is ready to interact with an object */ - bool m_ReadyToDig; + bool m_ReadyToInteract; /** * The position that this pet was spawned at @@ -497,7 +485,7 @@ class PetComponent : public Component /** * The halting radius of the pet while following a player */ - float m_FollowRadius; + const float m_FollowRadius = 8.0f; /** * The movement AI component that is related to this pet, required to move it around diff --git a/dScripts/02_server/Map/General/PetDigServer.cpp b/dScripts/02_server/Map/General/PetDigServer.cpp index 67109de8a..e3c5bbe41 100644 --- a/dScripts/02_server/Map/General/PetDigServer.cpp +++ b/dScripts/02_server/Map/General/PetDigServer.cpp @@ -108,14 +108,14 @@ void PetDigServer::OnUse(Entity* self, Entity* user) { auto* petComponent = PetComponent::GetActivePet(user->GetObjectID()); if (!petComponent) return; - if(petComponent->GetIsReadyToDig()) { // TODO: Add handling of the "first time" dig message + if(petComponent->GetIsReadyToInteract()) { // TODO: Add handling of the "first time" dig message auto* destroyableComponent = user->GetComponent(); if (!destroyableComponent) return; auto imagination = destroyableComponent->GetImagination(); if (imagination == 0) return; // TODO: Check if there was special behavior for this in the live game (PR_NEED_IMAGINATION) - petComponent->StartInteractDig(); + petComponent->StartInteractTreasureDig(); imagination -= 1; // TODO: Get rid of this magic number destroyableComponent->SetImagination(imagination); From 68df8af7848da8d31654e923e91d1aee6fab6c68 Mon Sep 17 00:00:00 2001 From: jadebenn Date: Wed, 13 Dec 2023 00:17:53 -0600 Subject: [PATCH 25/73] capitalization --- dGame/dComponents/PetComponent.cpp | 2 +- dGame/dComponents/PetComponent.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index a5d75de53..95f4c29a7 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -106,7 +106,7 @@ PetComponent::PetComponent(Entity* parent, uint32_t componentId): Component(pare auto result = query.execQuery(); if (!result.eof()) { - m_walkSpeed = result.getFloatField(0, 2.5f); + m_WalkSpeed = result.getFloatField(0, 2.5f); m_RunSpeed = result.getFloatField(1, 5.0f); m_SprintSpeed = result.getFloatField(2, 10.0f); imaginationDrainRate = result.getFloatField(3, 60.0f); diff --git a/dGame/dComponents/PetComponent.h b/dGame/dComponents/PetComponent.h index 687da810f..1adc527d7 100644 --- a/dGame/dComponents/PetComponent.h +++ b/dGame/dComponents/PetComponent.h @@ -505,7 +505,7 @@ class PetComponent : public Component /** * The walk speed of the pet */ - float m_walkSpeed; + float m_WalkSpeed; /** * The run speed of the pet From fd5ea75ead0b26cc1d3b080d54402df87f652967 Mon Sep 17 00:00:00 2001 From: jadebenn Date: Wed, 13 Dec 2023 00:56:41 -0600 Subject: [PATCH 26/73] added bone dragon treasure dig handling --- dGame/dComponents/PetComponent.cpp | 34 ++++-------------------------- 1 file changed, 4 insertions(+), 30 deletions(-) diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index 95f4c29a7..09ad37637 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -159,7 +159,6 @@ void PetComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpd void PetComponent::SetPetAiState(PetAiState newState) { if (newState == GetPetAiState()) return; this->m_State = newState; - LOG_DEBUG("Set pet AI state!"); } void PetComponent::OnUse(Entity* originator) { @@ -331,13 +330,6 @@ void PetComponent::Update(float deltaTime) { /*if (m_Owner == LWOOBJID_EMPTY) { UpdateUnowned(deltaTime); return; - } - - // Determine pet owner - auto* owner = GetOwner(); - if (!owner) { - m_Parent->Kill(); // Kill pet if no owner - return; }*/ // Update timers @@ -373,7 +365,6 @@ void PetComponent::Update(float deltaTime) { break; case PetAiState::goToObj: - LOG_DEBUG("Going to object!"); if (m_MovementAI->AtFinalWaypoint()) { LOG_DEBUG("Reached object!"); m_MovementAI->Stop(); @@ -498,25 +489,6 @@ void PetComponent::UpdateUnowned(float deltaTime) { //TODO: CURRENTLY UNUSED } } -/*void PetComponent::SetIsReadyToDig(bool isReady) { - if (isReady) { - LOG("Dig state reached!"); - //m_Interaction = closestTresure->GetObjectID(); - //SetAbility(PetAbilityType::JumpOnObject); - SetStatus(PetStatus::IS_NOT_WAITING); // Treasure dig status - m_ReadyToDig = true; - Game::entityManager->SerializeEntity(m_Parent); - } - else { - LOG("Dig state ended!"); - //m_Interaction = LWOOBJID_EMPTY; - //SetAbility(PetAbilityType::Invalid); - SetStatus(0); // TODO: Check status - m_ReadyToDig = false; - Game::entityManager->SerializeEntity(m_Parent); - } -}*/ - void PetComponent::TryBuild(uint32_t numBricks, bool clientFailed) { if (m_Tamer == LWOOBJID_EMPTY) return; @@ -922,7 +894,8 @@ void PetComponent::OnFollow() { const bool digUnlocked = missionComponent->GetMissionState(842) == eMissionState::COMPLETE; Entity* closestTreasure = PetDigServer::GetClosestTresure(ownerPos); - if (closestTreasure != nullptr && digUnlocked) { + const bool nonDragonForBone = closestTreasure->GetLOT() == 12192 && m_Parent->GetLOT() != 13067; + if (closestTreasure != nullptr && digUnlocked && !nonDragonForBone) { const NiPoint3 treasurePos = closestTreasure->GetPosition(); const LWOOBJID treasureID = closestTreasure->GetObjectID(); const float distance = Vector3::DistanceSquared(ownerPos, treasurePos); @@ -1022,6 +995,7 @@ void PetComponent::SetupInteractTreasureDig() { SetIsReadyToInteract(true); + SetAbility(PetAbilityType::JumpOnObject); SetStatus(PetStatus::IS_NOT_WAITING); // TODO: Double-check this is the right flag being set Game::entityManager->SerializeEntity(m_Parent); // TODO: Double-check pet packet captures m_Timer += 0.5f; @@ -1208,6 +1182,7 @@ void PetComponent::Command(NiPoint3 position, LWOOBJID source, int32_t commandTy // Emotes GameMessages::SendPlayEmote(m_Parent->GetObjectID(), typeId, owner->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS); } else if (commandType == 3) { + StopInteract(); // TODO: Verify this is necessary SetPetAiState(PetAiState::follow); } else if (commandType == 6) { // TODO: Go to player @@ -1220,7 +1195,6 @@ void PetComponent::Command(NiPoint3 position, LWOOBJID source, int32_t commandTy // Add movement functionality if (position != NiPoint3::ZERO) { m_MovementAI->SetDestination(position); - //m_Timer = 9; //Is this setting how long until the next update tick? } } From 291fc83dc87625fdd2deeb34038fd285799a17c4 Mon Sep 17 00:00:00 2001 From: jadebenn Date: Wed, 13 Dec 2023 00:57:01 -0600 Subject: [PATCH 27/73] added bone dragon treasure dig handling --- dGame/dComponents/PetComponent.cpp | 42 +----------------------------- 1 file changed, 1 insertion(+), 41 deletions(-) diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index 09ad37637..86be11ff2 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -384,7 +384,6 @@ void PetComponent::Update(float deltaTime) { } /* - auto destination = owner->GetPosition(); NiPoint3 position = m_MovementAI->GetParent()->GetPosition(); @@ -424,46 +423,7 @@ void PetComponent::Update(float deltaTime) { destination = switchPosition; } } - - // Determine if the "Lost Tags" mission has been completed and digging has been unlocked - auto* missionComponent = owner->GetComponent(); - if (!missionComponent) return; - const bool digUnlocked = missionComponent->GetMissionState(842) == eMissionState::COMPLETE; - - Entity* closestTresure = PetDigServer::GetClosestTresure(position); - if (closestTresure != nullptr && digUnlocked) { - // Skeleton Dragon Pat special case for bone digging - if (closestTresure->GetLOT() == 12192 && m_Parent->GetLOT() != 13067) { - goto skipTresure; - } - - NiPoint3 tresurePosition = closestTresure->GetPosition(); - float distance = Vector3::DistanceSquared(position, tresurePosition); - if (distance < 5 * 5) { - m_Interaction = closestTresure->GetObjectID(); - - Command(NiPoint3::ZERO, LWOOBJID_EMPTY, 1, PetEmote::Bounce, true); // Plays 'bounce' animation - - SetIsReadyToDig(true); - - } else if (distance < 10 * 10) { - haltDistance = 1; - - destination = tresurePosition; - - SetIsReadyToDig(false); - } - } - -skipTresure: - - m_MovementAI->SetHaltDistance(haltDistance); - - //m_MovementAI->SetMaxSpeed(2.5f); - - m_MovementAI->SetDestination(destination); - - m_Timer = 1;*/ + */ } void PetComponent::UpdateUnowned(float deltaTime) { //TODO: CURRENTLY UNUSED From 775e4fee9ec0b9d0d88ce55db7ef8378265aece9 Mon Sep 17 00:00:00 2001 From: jadebenn Date: Wed, 13 Dec 2023 01:01:34 -0600 Subject: [PATCH 28/73] change AND priority --- dGame/dComponents/PetComponent.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index 86be11ff2..d1ddc9a3f 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -855,7 +855,7 @@ void PetComponent::OnFollow() { Entity* closestTreasure = PetDigServer::GetClosestTresure(ownerPos); const bool nonDragonForBone = closestTreasure->GetLOT() == 12192 && m_Parent->GetLOT() != 13067; - if (closestTreasure != nullptr && digUnlocked && !nonDragonForBone) { + if (!nonDragonForBone && closestTreasure != nullptr && digUnlocked) { const NiPoint3 treasurePos = closestTreasure->GetPosition(); const LWOOBJID treasureID = closestTreasure->GetObjectID(); const float distance = Vector3::DistanceSquared(ownerPos, treasurePos); From 2a4cfe34474e5adbd2094455b07e7b639a1cd1b8 Mon Sep 17 00:00:00 2001 From: jadebenn Date: Wed, 13 Dec 2023 01:06:58 -0600 Subject: [PATCH 29/73] function rename --- dGame/dComponents/PetComponent.cpp | 4 ++-- dGame/dComponents/PetComponent.h | 2 +- dScripts/02_server/Map/General/PetDigServer.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index d1ddc9a3f..f3ba71d91 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -896,12 +896,12 @@ void PetComponent::OnInteract() { switch (GetInteractType()) { case PetInteractType::bouncer: - if (GetIsReadyToInteract()) LOG_DEBUG("Add the HandleInteractBouncer()!"); + if (IsReadyToInteract()) LOG_DEBUG("Add the HandleInteractBouncer()!"); else SetupInteractBouncer(); break; case PetInteractType::treasure: - if (GetIsReadyToInteract()) HandleInteractTreasureDig(); + if (IsReadyToInteract()) HandleInteractTreasureDig(); else SetupInteractTreasureDig(); break; diff --git a/dGame/dComponents/PetComponent.h b/dGame/dComponents/PetComponent.h index 1adc527d7..dcad34c16 100644 --- a/dGame/dComponents/PetComponent.h +++ b/dGame/dComponents/PetComponent.h @@ -272,7 +272,7 @@ class PetComponent : public Component /** * @return is pet ready to interact with an object */ - bool GetIsReadyToInteract() { return m_ReadyToInteract; }; + bool IsReadyToInteract() { return m_ReadyToInteract; }; /** * Set up the pet bouncer interaction diff --git a/dScripts/02_server/Map/General/PetDigServer.cpp b/dScripts/02_server/Map/General/PetDigServer.cpp index e3c5bbe41..ffd23cdc7 100644 --- a/dScripts/02_server/Map/General/PetDigServer.cpp +++ b/dScripts/02_server/Map/General/PetDigServer.cpp @@ -108,7 +108,7 @@ void PetDigServer::OnUse(Entity* self, Entity* user) { auto* petComponent = PetComponent::GetActivePet(user->GetObjectID()); if (!petComponent) return; - if(petComponent->GetIsReadyToInteract()) { // TODO: Add handling of the "first time" dig message + if(petComponent->IsReadyToInteract()) { // TODO: Add handling of the "first time" dig message auto* destroyableComponent = user->GetComponent(); if (!destroyableComponent) return; From 1b8f10fcfb89d2353838247cb2c9a1fd7ec10d2b Mon Sep 17 00:00:00 2001 From: jadebenn Date: Thu, 14 Dec 2023 01:33:49 -0600 Subject: [PATCH 30/73] added treasure dig menu prompts and help messages --- dCommon/dEnums/eHelpType.h | 6 +- dCommon/dEnums/ePetAbilityType.h | 10 ++ dGame/dComponents/BaseCombatAIComponent.h | 5 +- dGame/dComponents/PetComponent.cpp | 120 ++++++++++++------ dGame/dComponents/PetComponent.h | 40 +++--- dGame/dGameMessages/GameMessages.cpp | 17 ++- dGame/dGameMessages/GameMessages.h | 10 +- .../02_server/Map/General/PetDigServer.cpp | 14 +- dZoneManager/dZoneManager.cpp | 4 + dZoneManager/dZoneManager.h | 1 + 10 files changed, 145 insertions(+), 82 deletions(-) create mode 100644 dCommon/dEnums/ePetAbilityType.h diff --git a/dCommon/dEnums/eHelpType.h b/dCommon/dEnums/eHelpType.h index d1838cc67..94f584e62 100644 --- a/dCommon/dEnums/eHelpType.h +++ b/dCommon/dEnums/eHelpType.h @@ -1,6 +1,4 @@ - -#ifndef __EHELPTYPE__H__ -#define __EHELPTYPE__H__ +#pragma once #include @@ -37,5 +35,3 @@ enum class eHelpType : int32_t { PET_DESPAWN_TAMING_NEW_PET = 70, UI_INVENTORY_FULL_CANNOT_PICKUP_ITEM = 86 }; - -#endif //!__EHELPTYPE__H__ diff --git a/dCommon/dEnums/ePetAbilityType.h b/dCommon/dEnums/ePetAbilityType.h new file mode 100644 index 000000000..70cf75c6c --- /dev/null +++ b/dCommon/dEnums/ePetAbilityType.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +enum class ePetAbilityType : int32_t { + Invalid, + GoToObject, + JumpOnObject, + DigAtPosition +}; diff --git a/dGame/dComponents/BaseCombatAIComponent.h b/dGame/dComponents/BaseCombatAIComponent.h index a08b008e4..f85fa70f4 100644 --- a/dGame/dComponents/BaseCombatAIComponent.h +++ b/dGame/dComponents/BaseCombatAIComponent.h @@ -1,5 +1,4 @@ -#ifndef BASECOMBATAICOMPONENT_H -#define BASECOMBATAICOMPONENT_H +#pragma once #include "RakNetTypes.h" #include "dCommonVars.h" @@ -388,5 +387,3 @@ class BaseCombatAIComponent : public Component { */ bool IsMech(); }; - -#endif // BASECOMBATAICOMPONENT_H diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index f3ba71d91..f504da6f2 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -17,12 +17,15 @@ #include "eUnequippableActiveType.h" #include "eTerminateType.h" #include "ePetTamingNotifyType.h" +#include "ePetAbilityType.h" #include "eUseItemResponse.h" #include "ePlayerFlag.h" +#include "eHelpType.h" #include "Game.h" #include "dConfig.h" #include "dChatFilter.h" +#include "dZoneManager.h" #include "Database.h" #include "EntityInfo.h" #include "eMissionTaskType.h" @@ -34,6 +37,7 @@ std::unordered_map PetComponent::buildCache{}; std::unordered_map PetComponent::currentActivities{}; std::unordered_map PetComponent::activePets{}; +float PetComponent::m_FollowRadius{}; /** * Maps all the pet lots to a flag indicating that the player has caught it. All basic pets have been guessed by ObjID @@ -83,14 +87,15 @@ PetComponent::PetComponent(Entity* parent, uint32_t componentId): Component(pare m_TimerBounce = 0; m_DatabaseId = LWOOBJID_EMPTY; m_Status = PetStatus::TAMEABLE; // Tameable - m_Ability = PetAbilityType::Invalid; + m_Ability = ePetAbilityType::Invalid; m_StartPosition = m_Parent->GetPosition(); //NiPoint3::ZERO; m_MovementAI = nullptr; m_Preconditions = nullptr; m_ReadyToInteract = false; SetPetAiState(PetAiState::spawn); - //m_FollowRadius = 8.0f; + m_FollowRadius = Game::zoneManager->GetPetFollowRadius(); + SetIsHandlingInteraction(false); std::string checkPreconditions = GeneralUtils::UTF16ToWTF8(parent->GetVar(u"CheckPrecondition")); @@ -121,7 +126,7 @@ void PetComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpd outBitStream->Write1(); // Always serialize as dirty for now outBitStream->Write(static_cast(m_Status)); - outBitStream->Write(static_cast(tamed ? m_Ability : PetAbilityType::Invalid)); // Something with the overhead icon? + outBitStream->Write(static_cast(tamed ? m_Ability : ePetAbilityType::Invalid)); // Something with the overhead icon? const bool interacting = m_Interaction != LWOOBJID_EMPTY; @@ -164,6 +169,23 @@ void PetComponent::SetPetAiState(PetAiState newState) { void PetComponent::OnUse(Entity* originator) { LOG("PET USE!"); + if (IsReadyToInteract()) { + switch (GetAbility()) { + case ePetAbilityType::DigAtPosition: // Treasure dig TODO: FIX ICON + StartInteractTreasureDig(); + break; + + case ePetAbilityType::JumpOnObject: // Bouncer + //StartInteractBouncer(); + break; + + default: + break; + } + } + + // TODO: Rewrite everything below this comment + if (m_Owner != LWOOBJID_EMPTY) return; if (m_Tamer != LWOOBJID_EMPTY) { @@ -526,10 +548,7 @@ void PetComponent::NotifyTamingBuildSuccess(NiPoint3 position) { GameMessages::SendPetResponse(m_Tamer, m_Parent->GetObjectID(), 0, 10, 0, tamer->GetSystemAddress()); auto* inventoryComponent = tamer->GetComponent(); - - if (inventoryComponent == nullptr) { - return; - } + if (!inventoryComponent) return; LWOOBJID petSubKey = ObjectIDManager::Instance()->GenerateRandomObjectID(); @@ -548,11 +567,9 @@ void PetComponent::NotifyTamingBuildSuccess(NiPoint3 position) { GameMessages::SendRegisterPetDBID(m_Tamer, petSubKey, tamer->GetSystemAddress()); inventoryComponent->AddItem(m_Parent->GetLOT(), 1, eLootSourceType::ACTIVITY, eInventoryType::MODELS, {}, LWOOBJID_EMPTY, true, false, petSubKey); + auto* item = inventoryComponent->FindItemBySubKey(petSubKey, MODELS); - - if (item == nullptr) { - return; - } + if (!item) return; DatabasePet databasePet{}; @@ -626,10 +643,7 @@ void PetComponent::RequestSetPetName(std::u16string name) { LOG("Got set pet name (%s)", GeneralUtils::UTF16ToWTF8(name).c_str()); auto* inventoryComponent = tamer->GetComponent(); - - if (inventoryComponent == nullptr) { - return; - } + if (!inventoryComponent) return; m_ModerationStatus = 1; // Pending m_Name = ""; @@ -769,9 +783,7 @@ void PetComponent::ClientFailTamingMinigame() { } void PetComponent::Wander() { - //m_MovementAI = m_Parent->GetComponent(); - - if (/*m_MovementAI == nullptr ||*/ !m_MovementAI->AtFinalWaypoint()) return; + if (!m_MovementAI->AtFinalWaypoint()) return; m_MovementAI->SetHaltDistance(0); @@ -916,7 +928,7 @@ void PetComponent::OnInteract() { void PetComponent::StartInteract(const NiPoint3 position, const PetInteractType interactType, const LWOOBJID interactID) { SetInteraction(interactID); // TODO: Check if this should be serialized for goToObj SetInteractType(interactType); - SetAbility(PetAbilityType::GoToObject); + SetAbility(ePetAbilityType::GoToObject); SetPetAiState(PetAiState::goToObj); m_MovementAI->SetMaxSpeed(m_RunSpeed); m_MovementAI->SetHaltDistance(0.0f); @@ -926,21 +938,28 @@ void PetComponent::StartInteract(const NiPoint3 position, const PetInteractType } void PetComponent::StopInteract() { + Entity* owner = GetOwner(); + if (!owner) return; + const auto petAbility = ePetAbilityType::Invalid; + SetInteraction(LWOOBJID_EMPTY); SetInteractType(PetInteractType::none); - SetAbility(PetAbilityType::Invalid); + SetAbility(petAbility); SetPetAiState(PetAiState::follow); SetStatus(PetStatus::NONE); SetIsReadyToInteract(false); + SetIsHandlingInteraction(false); // Needed? m_MovementAI->SetMaxSpeed(m_SprintSpeed); m_MovementAI->SetHaltDistance(m_FollowRadius); LOG_DEBUG("Stopping interaction!"); + Game::entityManager->SerializeEntity(m_Parent); + GameMessages::SendShowPetActionButton(m_Owner, petAbility, false, owner->GetSystemAddress()); // Needed? } void PetComponent::SetupInteractBouncer() { // THIS IS ALL BAD, BAD, BAD! FIX IT, ME! >:( - /*SetAbility(PetAbilityType::JumpOnObject); + /*SetAbility(ePetAbilityType::JumpOnObject); NiPoint3 destination = m_MovementAI->GetDestination(); SwitchComponent* closestSwitch = SwitchComponent::GetClosestSwitch(destination); m_Interaction = closestSwitch->GetParentEntity()->GetObjectID(); @@ -949,37 +968,63 @@ void PetComponent::SetupInteractBouncer() { } void PetComponent::SetupInteractTreasureDig() { - LOG_DEBUG("Setting up dig interaction!"); - Entity* closestTreasure = Game::entityManager->GetEntity(GetInteraction()); - if (!closestTreasure) return; + auto* owner = GetOwner(); + if (!owner) return; + LOG_DEBUG("Setting up dig interaction!"); SetIsReadyToInteract(true); + auto petAbility = ePetAbilityType::DigAtPosition; - SetAbility(PetAbilityType::JumpOnObject); + SetAbility(petAbility); SetStatus(PetStatus::IS_NOT_WAITING); // TODO: Double-check this is the right flag being set Game::entityManager->SerializeEntity(m_Parent); // TODO: Double-check pet packet captures + + const auto sysAddr = owner->GetSystemAddress(); + GameMessages::SendHelp(m_Owner, eHelpType::PR_DIG_TUTORIAL_01, sysAddr); + GameMessages::SendShowPetActionButton(m_Owner, petAbility, true, sysAddr); m_Timer += 0.5f; } void PetComponent::StartInteractTreasureDig() { - SetAbility(PetAbilityType::DigAtPosition); + Entity* user = GetOwner(); + if (IsHandlingInteraction() || !user) return; + + auto* destroyableComponent = user->GetComponent(); + if (!destroyableComponent) return; + + auto imagination = destroyableComponent->GetImagination(); + int32_t imaginationCost = 1; // TODO: Get rid of this magic number - make static variable from lookup + if (imagination < imaginationCost) { + //GameMessages::SendHelp(user->GetObjectID(), eHelpType::PR_NEED_IMAGINATION, user->GetSystemAddress()); // Check if right message! + return; + } + + GameMessages::SendShowPetActionButton(m_Owner, ePetAbilityType::Invalid, false, user->GetSystemAddress()); + + imagination -= imaginationCost; + destroyableComponent->SetImagination(imagination); + Game::entityManager->SerializeEntity(user); + + SetIsHandlingInteraction(true); + auto newStatus = GeneralUtils::ClearBit(GetStatus(), 6); + SetStatus(newStatus); // TODO: FIND THE CORRECT STATUS TO USE HERE Game::entityManager->SerializeEntity(m_Parent); Command(NiPoint3::ZERO, LWOOBJID_EMPTY, 1, PetEmote::DigTreasure, true); // Plays 'dig' animation - m_Timer = 1.5f; + m_Timer = 2.0f; } void PetComponent::HandleInteractTreasureDig() { - if (GetAbility() == PetAbilityType::DigAtPosition) { + if (IsHandlingInteraction()) { auto* owner = GetOwner(); - auto* treasure = Game::entityManager->GetEntity(GetInteraction()); - if (!treasure) return; + if (!treasure || !owner) return; treasure->Smash(m_Parent->GetObjectID()); LOG_DEBUG("Pet dig completed!"); + GameMessages::SendHelp(m_Owner, eHelpType::PR_DIG_TUTORIAL_03, owner->GetSystemAddress()); StopInteract(); //TODO: This may not be totally consistent with live behavior, where the pet seems to stay near the dig and not immediately follow - m_Timer = 1.5f; + //m_Timer = 1.5f; return; } @@ -991,7 +1036,7 @@ void PetComponent::HandleInteractTreasureDig() { m_Timer += 0.5f; } -void PetComponent::Activate(Item* item, bool registerPet, bool fromTaming) { +void PetComponent::Activate(Item* item, bool registerPet, bool fromTaming) { // TODO: Offset spawn position so it's not on top of player char AddDrainImaginationTimer(item, fromTaming); m_ItemId = item->GetId(); @@ -1041,8 +1086,6 @@ void PetComponent::Activate(Item* item, bool registerPet, bool fromTaming) { activePets[m_Owner] = m_Parent->GetObjectID(); - //m_Timer = 3; - Game::entityManager->SerializeEntity(m_Parent); owner->GetCharacter()->SetPlayerFlag(ePlayerFlag::FIRST_MANUAL_PET_HIBERNATE, true); @@ -1054,8 +1097,6 @@ void PetComponent::Activate(Item* item, bool registerPet, bool fromTaming) { GameMessages::SendRegisterPetDBID(m_Owner, m_DatabaseId, owner->GetSystemAddress()); } - - GameMessages::SendShowPetActionButton(m_Owner, 3, true, owner->GetSystemAddress()); } void PetComponent::AddDrainImaginationTimer(Item* item, bool fromTaming) { @@ -1115,7 +1156,7 @@ void PetComponent::Deactivate() { GameMessages::SendRegisterPetDBID(m_Owner, LWOOBJID_EMPTY, owner->GetSystemAddress()); - GameMessages::SendShowPetActionButton(m_Owner, 0, false, owner->GetSystemAddress()); + GameMessages::SendShowPetActionButton(m_Owner, ePetAbilityType::Invalid, false, owner->GetSystemAddress()); } void PetComponent::Release() { @@ -1182,7 +1223,7 @@ uint32_t PetComponent::GetStatus() const { return m_Status; } -PetAbilityType PetComponent::GetAbility() const { +ePetAbilityType PetComponent::GetAbility() const { return m_Ability; } @@ -1192,9 +1233,10 @@ void PetComponent::SetInteraction(LWOOBJID value) { void PetComponent::SetStatus(uint32_t value) { m_Status = value; + LOG_DEBUG("Pet status set to: %x", m_Status); } -void PetComponent::SetAbility(PetAbilityType value) { +void PetComponent::SetAbility(ePetAbilityType value) { m_Ability = value; } diff --git a/dGame/dComponents/PetComponent.h b/dGame/dComponents/PetComponent.h index dcad34c16..12f8813bd 100644 --- a/dGame/dComponents/PetComponent.h +++ b/dGame/dComponents/PetComponent.h @@ -5,6 +5,7 @@ #include "Component.h" #include "Preconditions.h" #include "eReplicaComponentType.h" +#include "ePetAbilityType.h" /* * The current state of the pet AI @@ -45,13 +46,6 @@ enum PetEmote : int32_t { Bounce }; -enum class PetAbilityType { - Invalid, - GoToObject, - JumpOnObject, - DigAtPosition -}; - /** * Represents an entity that is a pet. This pet can be tamed and consequently follows the tamer around, allowing it * to dig for treasure and activate pet bouncers. @@ -249,13 +243,13 @@ class PetComponent : public Component * Returns an ability the pet may perform, currently unused * @return an ability the pet may perform */ - PetAbilityType GetAbility() const; + ePetAbilityType GetAbility() const; /** * Sets the ability of the pet, currently unused * @param value the ability to set */ - void SetAbility(PetAbilityType value); + void SetAbility(ePetAbilityType value); /** * Sets preconditions for the pet that need to be met before it can be tamed @@ -274,6 +268,17 @@ class PetComponent : public Component */ bool IsReadyToInteract() { return m_ReadyToInteract; }; + /** + * Sets if the pet is currently handling an interaction with an object + * @param isHandlingInteraction whether the pet is currently handling an interaction with an object + */ + void SetIsHandlingInteraction(bool isHandlingInteraction) { m_IsHandlingInteraction = isHandlingInteraction; }; + + /** + * @return is pet currently handling an interaction with an object + */ + bool IsHandlingInteraction() { return m_IsHandlingInteraction; }; + /** * Set up the pet bouncer interaction */ @@ -387,6 +392,11 @@ class PetComponent : public Component */ static std::map petFlags; + /** + * The halting radius of the pet while following a player + */ + static float m_FollowRadius; + /** * The ID of the component in the pet component table */ @@ -455,7 +465,7 @@ class PetComponent : public Component /** * A currently active ability, mostly unused */ - PetAbilityType m_Ability; + ePetAbilityType m_Ability; /** * The time an entity has left to complete the minigame @@ -477,16 +487,16 @@ class PetComponent : public Component */ bool m_ReadyToInteract; + /** + * Boolean that sets if a pet is currently handling an interaction with an object + */ + bool m_IsHandlingInteraction; + /** * The position that this pet was spawned at */ NiPoint3 m_StartPosition; - /** - * The halting radius of the pet while following a player - */ - const float m_FollowRadius = 8.0f; - /** * The movement AI component that is related to this pet, required to move it around */ diff --git a/dGame/dGameMessages/GameMessages.cpp b/dGame/dGameMessages/GameMessages.cpp index fd174a2b4..821f9b6f1 100644 --- a/dGame/dGameMessages/GameMessages.cpp +++ b/dGame/dGameMessages/GameMessages.cpp @@ -44,6 +44,8 @@ #include "eStateChangeType.h" #include "eConnectionType.h" #include "ePlayerFlag.h" +#include "eHelpType.h" +#include "ePetAbilityType.h" #include #include @@ -505,6 +507,17 @@ void GameMessages::SendNotifyClientFlagChange(const LWOOBJID& objectID, uint32_t SEND_PACKET; } +void GameMessages::SendHelp(const LWOOBJID& objectID, const eHelpType help, const SystemAddress& sysAddr) { + CBITSTREAM; + CMSGHEADER; + + bitStream.Write(objectID); + bitStream.Write(eGameMessageType::HELP); + bitStream.Write(help); + + SEND_PACKET; +} + void GameMessages::SendChangeObjectWorldState(const LWOOBJID& objectID, eObjectWorldState state, const SystemAddress& sysAddr) { CBITSTREAM; CMSGHEADER; @@ -3508,14 +3521,14 @@ void GameMessages::SendClientExitTamingMinigame(LWOOBJID objectId, bool bVolunta SEND_PACKET; } -void GameMessages::SendShowPetActionButton(LWOOBJID objectId, int32_t buttonLabel, bool bShow, const SystemAddress& sysAddr) { +void GameMessages::SendShowPetActionButton(const LWOOBJID& objectId, const ePetAbilityType petAbility, bool bShow, const SystemAddress& sysAddr) { CBITSTREAM; CMSGHEADER; bitStream.Write(objectId); bitStream.Write(eGameMessageType::SHOW_PET_ACTION_BUTTON); - bitStream.Write(buttonLabel); + bitStream.Write(petAbility); bitStream.Write(bShow); if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST; diff --git a/dGame/dGameMessages/GameMessages.h b/dGame/dGameMessages/GameMessages.h index 5199a72ab..641f4b9a0 100644 --- a/dGame/dGameMessages/GameMessages.h +++ b/dGame/dGameMessages/GameMessages.h @@ -1,5 +1,4 @@ -#ifndef GAMEMESSAGES_H -#define GAMEMESSAGES_H +#pragma once #include "dCommonVars.h" #include @@ -10,6 +9,8 @@ #include "eEndBehavior.h" #include "eCyclingMode.h" #include "eLootSourceType.h" +#include "eHelpType.h" +#include "ePetAbilityType.h" #include "Brick.h" class AMFBaseValue; @@ -81,6 +82,7 @@ namespace GameMessages { void SendAddItemToInventoryClientSync(Entity* entity, const SystemAddress& sysAddr, Item* item, const LWOOBJID& objectID, bool showFlyingLoot, int itemCount, LWOOBJID subKey = LWOOBJID_EMPTY, eLootSourceType lootSourceType = eLootSourceType::NONE); void SendNotifyClientFlagChange(const LWOOBJID& objectID, uint32_t iFlagID, bool bFlag, const SystemAddress& sysAddr); + void SendHelp(const LWOOBJID& objectID, const eHelpType help, const SystemAddress& sysAddr); void SendChangeObjectWorldState(const LWOOBJID& objectID, eObjectWorldState state, const SystemAddress& sysAddr); void SendOfferMission(const LWOOBJID& entity, const SystemAddress& sysAddr, int32_t missionID, const LWOOBJID& offererID); @@ -385,7 +387,7 @@ namespace GameMessages { void SendClientExitTamingMinigame(LWOOBJID objectId, bool bVoluntaryExit, const SystemAddress& sysAddr); - void SendShowPetActionButton(LWOOBJID objectId, int32_t buttonLabel, bool bShow, const SystemAddress& sysAddr); + void SendShowPetActionButton(const LWOOBJID& objectId, const ePetAbilityType petAbility, bool bShow, const SystemAddress& sysAddr); void SendPlayEmote(LWOOBJID objectId, int32_t emoteID, LWOOBJID target, const SystemAddress& sysAddr); @@ -659,5 +661,3 @@ namespace GameMessages { void HandleConfirmDonationOnPlayer(RakNet::BitStream* inStream, Entity* entity); void HandleCancelDonationOnPlayer(RakNet::BitStream* inStream, Entity* entity); }; - -#endif // GAMEMESSAGES_H diff --git a/dScripts/02_server/Map/General/PetDigServer.cpp b/dScripts/02_server/Map/General/PetDigServer.cpp index ffd23cdc7..43c00f7f6 100644 --- a/dScripts/02_server/Map/General/PetDigServer.cpp +++ b/dScripts/02_server/Map/General/PetDigServer.cpp @@ -103,23 +103,13 @@ void PetDigServer::OnDie(Entity* self, Entity* killer) { } void PetDigServer::OnUse(Entity* self, Entity* user) { - LOG("Treasure used!"); + LOG_DEBUG("Treasure used! LWOOBJID: %d", self->GetObjectID()); auto* petComponent = PetComponent::GetActivePet(user->GetObjectID()); if (!petComponent) return; - if(petComponent->IsReadyToInteract()) { // TODO: Add handling of the "first time" dig message - auto* destroyableComponent = user->GetComponent(); - if (!destroyableComponent) return; - - auto imagination = destroyableComponent->GetImagination(); - if (imagination == 0) return; // TODO: Check if there was special behavior for this in the live game (PR_NEED_IMAGINATION) - + if(petComponent->IsReadyToInteract()) { petComponent->StartInteractTreasureDig(); - - imagination -= 1; // TODO: Get rid of this magic number - destroyableComponent->SetImagination(imagination); - Game::entityManager->SerializeEntity(user); } } diff --git a/dZoneManager/dZoneManager.cpp b/dZoneManager/dZoneManager.cpp index 137a9cab2..76a8b1fc8 100644 --- a/dZoneManager/dZoneManager.cpp +++ b/dZoneManager/dZoneManager.cpp @@ -226,6 +226,10 @@ std::vector dZoneManager::GetSpawnersInGroup(std::string group) { return spawnersInGroup; } +float dZoneManager::GetPetFollowRadius() { + return GetWorldConfig()->petFollowRadius; +} + uint32_t dZoneManager::GetUniqueMissionIdStartingValue() { if (m_UniqueMissionIdStart == 0) { auto tableData = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM Missions WHERE isMission = 0 GROUP BY isMission;"); diff --git a/dZoneManager/dZoneManager.h b/dZoneManager/dZoneManager.h index b5db3f6e0..21ef013ad 100644 --- a/dZoneManager/dZoneManager.h +++ b/dZoneManager/dZoneManager.h @@ -44,6 +44,7 @@ class dZoneManager { bool GetDisableSaveLocation() { return m_DisableSaveLocation; } bool GetMountsAllowed() { return m_MountsAllowed; } bool GetPetsAllowed() { return m_PetsAllowed; } + float GetPetFollowRadius(); uint32_t GetUniqueMissionIdStartingValue(); bool CheckIfAccessibleZone(LWOMAPID zoneID); From 9953255ece0e05ac98fa36c7896b4f42a57a5af3 Mon Sep 17 00:00:00 2001 From: jadebenn Date: Thu, 14 Dec 2023 07:42:59 -0600 Subject: [PATCH 31/73] Update GameMessages.h --- dGame/dGameMessages/GameMessages.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dGame/dGameMessages/GameMessages.h b/dGame/dGameMessages/GameMessages.h index 641f4b9a0..f17919047 100644 --- a/dGame/dGameMessages/GameMessages.h +++ b/dGame/dGameMessages/GameMessages.h @@ -82,7 +82,7 @@ namespace GameMessages { void SendAddItemToInventoryClientSync(Entity* entity, const SystemAddress& sysAddr, Item* item, const LWOOBJID& objectID, bool showFlyingLoot, int itemCount, LWOOBJID subKey = LWOOBJID_EMPTY, eLootSourceType lootSourceType = eLootSourceType::NONE); void SendNotifyClientFlagChange(const LWOOBJID& objectID, uint32_t iFlagID, bool bFlag, const SystemAddress& sysAddr); - void SendHelp(const LWOOBJID& objectID, const eHelpType help, const SystemAddress& sysAddr); + void SendHelp(const LWOOBJID& objectId, const eHelpType help, const SystemAddress& sysAddr); void SendChangeObjectWorldState(const LWOOBJID& objectID, eObjectWorldState state, const SystemAddress& sysAddr); void SendOfferMission(const LWOOBJID& entity, const SystemAddress& sysAddr, int32_t missionID, const LWOOBJID& offererID); @@ -387,7 +387,7 @@ namespace GameMessages { void SendClientExitTamingMinigame(LWOOBJID objectId, bool bVoluntaryExit, const SystemAddress& sysAddr); - void SendShowPetActionButton(const LWOOBJID& objectId, const ePetAbilityType petAbility, bool bShow, const SystemAddress& sysAddr); + void SendShowPetActionButton(const LWOOBJID& objectId, const ePetAbilityType petAbility, const bool bShow, const SystemAddress& sysAddr); void SendPlayEmote(LWOOBJID objectId, int32_t emoteID, LWOOBJID target, const SystemAddress& sysAddr); From 2db76ae58928bb49667f391206b814494665afc2 Mon Sep 17 00:00:00 2001 From: jadebenn Date: Thu, 14 Dec 2023 07:46:27 -0600 Subject: [PATCH 32/73] Update GameMessages.cpp --- dGame/dGameMessages/GameMessages.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/dGame/dGameMessages/GameMessages.cpp b/dGame/dGameMessages/GameMessages.cpp index 821f9b6f1..da93de211 100644 --- a/dGame/dGameMessages/GameMessages.cpp +++ b/dGame/dGameMessages/GameMessages.cpp @@ -507,14 +507,14 @@ void GameMessages::SendNotifyClientFlagChange(const LWOOBJID& objectID, uint32_t SEND_PACKET; } -void GameMessages::SendHelp(const LWOOBJID& objectID, const eHelpType help, const SystemAddress& sysAddr) { +void GameMessages::SendHelp(const LWOOBJID& objectId, const eHelpType help, const SystemAddress& sysAddr) { CBITSTREAM; CMSGHEADER; - - bitStream.Write(objectID); - bitStream.Write(eGameMessageType::HELP); - bitStream.Write(help); - + + bitStream.Write(objectId); + bitStream.Write(eGameMessageType::HELP); + bitStream.Write(help); + SEND_PACKET; } @@ -3521,7 +3521,7 @@ void GameMessages::SendClientExitTamingMinigame(LWOOBJID objectId, bool bVolunta SEND_PACKET; } -void GameMessages::SendShowPetActionButton(const LWOOBJID& objectId, const ePetAbilityType petAbility, bool bShow, const SystemAddress& sysAddr) { +void GameMessages::SendShowPetActionButton(const LWOOBJID& objectId, const ePetAbilityType petAbility, const bool bShow, const SystemAddress& sysAddr) { CBITSTREAM; CMSGHEADER; @@ -3531,7 +3531,7 @@ void GameMessages::SendShowPetActionButton(const LWOOBJID& objectId, const ePetA bitStream.Write(petAbility); bitStream.Write(bShow); - if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST; + if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST; // TODO: Don't think this should ever be broadcasted, need to confirm SEND_PACKET; } From 8f19aa96d33324a0671efe319b2c5ebc66211787 Mon Sep 17 00:00:00 2001 From: jadebenn Date: Thu, 14 Dec 2023 08:04:41 -0600 Subject: [PATCH 33/73] minor gm formatting fix --- dGame/dGameMessages/GameMessages.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dGame/dGameMessages/GameMessages.cpp b/dGame/dGameMessages/GameMessages.cpp index da93de211..573e05dc5 100644 --- a/dGame/dGameMessages/GameMessages.cpp +++ b/dGame/dGameMessages/GameMessages.cpp @@ -1150,10 +1150,10 @@ void GameMessages::SendPlayerReachedRespawnCheckpoint(Entity* entity, const NiPo bitStream.Write(position.y); bitStream.Write(position.z); - const bool isNotIdentity = rotation != NiQuaternion::IDENTITY; - bitStream.Write(isNotIdentity); + const bool bIsNotIdentity = rotation != NiQuaternion::IDENTITY; + bitStream.Write(bIsNotIdentity); - if (isNotIdentity) { + if (bIsNotIdentity) { bitStream.Write(rotation.w); bitStream.Write(rotation.x); bitStream.Write(rotation.y); From f5ca142eb9d2f21b145c5b493eaf3f39fffac046 Mon Sep 17 00:00:00 2001 From: jadebenn Date: Thu, 14 Dec 2023 09:21:17 -0600 Subject: [PATCH 34/73] Update PetStatus enum to prep for bitset use --- dGame/dComponents/PetComponent.h | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/dGame/dComponents/PetComponent.h b/dGame/dComponents/PetComponent.h index 12f8813bd..79233876d 100644 --- a/dGame/dComponents/PetComponent.h +++ b/dGame/dComponents/PetComponent.h @@ -33,11 +33,10 @@ enum class PetInteractType : uint8_t { */ enum PetStatus : uint32_t { NONE, - //READY_TO_DIG, - BEING_TAMED = 0x10, - IS_NOT_WAITING = 0x20, - PLAY_SPAWN_ANIM = 0x80, - TAMEABLE = 0x4000000 + BEING_TAMED = 1 << 4, //0x10, + IS_NOT_WAITING = 1 << 5, //0x20, + PLAY_SPAWN_ANIM = 1 << 7, //0x80, + TAMEABLE = 1 << 8 //0x4000000 }; enum PetEmote : int32_t { From 668bebf68c9f4567e16057d2ffead3d78f0fa080 Mon Sep 17 00:00:00 2001 From: jadebenn Date: Thu, 14 Dec 2023 23:43:08 -0600 Subject: [PATCH 35/73] adding new pet flags --- dGame/dComponents/PetComponent.cpp | 22 +- dGame/dComponents/PetComponent.h | 61 +- .../dComponentsTests/CMakeLists.txt | 1 + .../dComponentsTests/PetComponentTests.cpp | 571 ++++++++++++++++++ 4 files changed, 630 insertions(+), 25 deletions(-) create mode 100644 tests/dGameTests/dComponentsTests/PetComponentTests.cpp diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index f504da6f2..a7937207a 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -86,7 +86,7 @@ PetComponent::PetComponent(Entity* parent, uint32_t componentId): Component(pare m_TimerAway = 0; m_TimerBounce = 0; m_DatabaseId = LWOOBJID_EMPTY; - m_Status = PetStatus::TAMEABLE; // Tameable + m_Flags = 1 << PetFlag::TAMEABLE; // Tameable m_Ability = ePetAbilityType::Invalid; m_StartPosition = m_Parent->GetPosition(); //NiPoint3::ZERO; m_MovementAI = nullptr; @@ -125,7 +125,7 @@ void PetComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpd outBitStream->Write1(); // Always serialize as dirty for now - outBitStream->Write(static_cast(m_Status)); + outBitStream->Write(static_cast(m_Flags)); outBitStream->Write(static_cast(tamed ? m_Ability : ePetAbilityType::Invalid)); // Something with the overhead icon? const bool interacting = m_Interaction != LWOOBJID_EMPTY; @@ -719,7 +719,7 @@ void PetComponent::ClientExitTamingMinigame(bool voluntaryExit) { currentActivities.erase(m_Tamer); - SetStatus(PetStatus::TAMEABLE); + SetStatus(1 << PetFlag::TAMEABLE); m_Tamer = LWOOBJID_EMPTY; m_Timer = 0; @@ -770,7 +770,7 @@ void PetComponent::ClientFailTamingMinigame() { currentActivities.erase(m_Tamer); - SetStatus(PetStatus::TAMEABLE); + SetStatus(1 << PetFlag::TAMEABLE); m_Tamer = LWOOBJID_EMPTY; m_Timer = 0; @@ -835,7 +835,7 @@ void PetComponent::OnSpawn() { m_Parent->SetOwnerOverride(m_Owner); m_MovementAI->SetMaxSpeed(m_SprintSpeed); m_MovementAI->SetHaltDistance(m_FollowRadius); - SetStatus(PetStatus::NONE); + SetStatus(1 << PetFlag::NONE); SetPetAiState(PetAiState::follow); } else { @@ -946,7 +946,7 @@ void PetComponent::StopInteract() { SetInteractType(PetInteractType::none); SetAbility(petAbility); SetPetAiState(PetAiState::follow); - SetStatus(PetStatus::NONE); + SetStatus(1 << PetFlag::NONE); SetIsReadyToInteract(false); SetIsHandlingInteraction(false); // Needed? m_MovementAI->SetMaxSpeed(m_SprintSpeed); @@ -976,7 +976,7 @@ void PetComponent::SetupInteractTreasureDig() { auto petAbility = ePetAbilityType::DigAtPosition; SetAbility(petAbility); - SetStatus(PetStatus::IS_NOT_WAITING); // TODO: Double-check this is the right flag being set + SetStatus(1 << PetFlag::NOT_WAITING); // TODO: Double-check this is the right flag being set Game::entityManager->SerializeEntity(m_Parent); // TODO: Double-check pet packet captures const auto sysAddr = owner->GetSystemAddress(); @@ -1053,7 +1053,7 @@ void PetComponent::Activate(Item* item, bool registerPet, bool fromTaming) { // auto* owner = GetOwner(); if (owner == nullptr) return; - SetStatus(PetStatus::PLAY_SPAWN_ANIM); + SetStatus(1 << PetFlag::PLAY_SPAWN_ANIM); auto databaseData = inventoryComponent->GetDatabasePet(m_DatabaseId); @@ -1220,7 +1220,7 @@ LWOOBJID PetComponent::GetItemId() const { } uint32_t PetComponent::GetStatus() const { - return m_Status; + return m_Flags; } ePetAbilityType PetComponent::GetAbility() const { @@ -1232,8 +1232,8 @@ void PetComponent::SetInteraction(LWOOBJID value) { } void PetComponent::SetStatus(uint32_t value) { - m_Status = value; - LOG_DEBUG("Pet status set to: %x", m_Status); + m_Flags = value; + LOG_DEBUG("Pet status set to: %x", m_Flags); } void PetComponent::SetAbility(ePetAbilityType value) { diff --git a/dGame/dComponents/PetComponent.h b/dGame/dComponents/PetComponent.h index 79233876d..2c11646de 100644 --- a/dGame/dComponents/PetComponent.h +++ b/dGame/dComponents/PetComponent.h @@ -1,4 +1,5 @@ -#pragma once +#ifndef PETCOMPONENT_H +#define PETCOMPONENT_H #include "Entity.h" #include "MovementAIComponent.h" @@ -22,23 +23,34 @@ enum class PetAiState : uint8_t { /* * The type of object the pet is interacting with */ -enum class PetInteractType : uint8_t { +enum PetInteractType : uint8_t { none, // Not interacting treasure, // Treasure dig bouncer // Bouncer switch }; -/* -* The status of the pet: Governs the icon above their head and the interactions available +/** + * The flags governing the status of the pet: Governs the icon above their head and the interactions available */ -enum PetStatus : uint32_t { +enum PetFlag : uint32_t { NONE, - BEING_TAMED = 1 << 4, //0x10, - IS_NOT_WAITING = 1 << 5, //0x20, - PLAY_SPAWN_ANIM = 1 << 7, //0x80, - TAMEABLE = 1 << 8 //0x4000000 + BEING_TAMED = 4, + NOT_WAITING = 5, + PLAY_SPAWN_ANIM = 7, + TAMEABLE = 8 }; +/* +* DEPRECATED The status of the pet: Governs the icon above their head and the interactions available +*/ +/*enum PetStatus : uint32_t { + NONE, + BEING_TAMED = 1 << 4, //PetFlag::BEING_TAMED, //0x10, + IS_NOT_WAITING = 1 << 5, //PetFlag::NOT_WAITING, //0x20, + PLAY_SPAWN_ANIM = 1 << 7, //PetFlag::PLAY_SPAWN_ANIM, //0x80, + TAMEABLE = 1 << 8 // PetFlag::TAMEABLE //0x4000000 +};*/ + enum PetEmote : int32_t { ActivateSwitch = 201, DigTreasure, @@ -49,12 +61,11 @@ enum PetEmote : int32_t { * Represents an entity that is a pet. This pet can be tamed and consequently follows the tamer around, allowing it * to dig for treasure and activate pet bouncers. */ -class PetComponent : public Component -{ +class PetComponent : public Component { public: inline static const eReplicaComponentType ComponentType = eReplicaComponentType::PET; - explicit PetComponent(Entity* parentEntity, uint32_t componentId); + PetComponent(Entity* parentEntity, uint32_t componentId); ~PetComponent() override; void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override; @@ -70,6 +81,26 @@ class PetComponent : public Component */ PetAiState GetPetAiState() { return m_State; }; + /** + * Template function for setting pet flags + */ + template + void SetFlag(T... flag) { + //m_Flags |= (uint32_t)flag; + m_Flags |= ((uint32_t)flag | ...);//(static_cast(flag) | ...); + //T::operator|(m_Flags, flag...); + } + + /** + * Template function for getting pet flags + */ + template + const bool HasFlag(T... flag) { + //return (m_Flags & (u_int32_t)flag) == (u_int32_t)flag; + //return T::operator&(m_Flags, flag...) == T::operator&(flag...); + return true; + } + void Update(float deltaTime) override; /** @@ -452,9 +483,9 @@ class PetComponent : public Component std::string m_OwnerName; /** - * The current state of the pet (e.g. tamable, tamed, etc). + * The current flags of the pet (e.g. tamable, tamed, etc). */ - uint32_t m_Status; + uint32_t m_Flags; /** * The current state of the pet AI @@ -526,3 +557,5 @@ class PetComponent : public Component */ float m_SprintSpeed; }; + +#endif // PETCOMPONENT_H diff --git a/tests/dGameTests/dComponentsTests/CMakeLists.txt b/tests/dGameTests/dComponentsTests/CMakeLists.txt index e38f7a535..374095af0 100644 --- a/tests/dGameTests/dComponentsTests/CMakeLists.txt +++ b/tests/dGameTests/dComponentsTests/CMakeLists.txt @@ -1,5 +1,6 @@ set(DCOMPONENTS_TESTS "DestroyableComponentTests.cpp" + "PetComponentTests.cpp" "SimplePhysicsComponentTests.cpp" ) diff --git a/tests/dGameTests/dComponentsTests/PetComponentTests.cpp b/tests/dGameTests/dComponentsTests/PetComponentTests.cpp new file mode 100644 index 000000000..56fe409f0 --- /dev/null +++ b/tests/dGameTests/dComponentsTests/PetComponentTests.cpp @@ -0,0 +1,571 @@ +#include "GameDependencies.h" +#include + +#include "BitStream.h" +#include "PetComponent.h" +#include "Entity.h" +#include "eReplicaComponentType.h" +#include "ePetAbilityType.h" +#include "eStateChangeType.h" + +// Table includes +#include "CDClientManager.h" +#include "CDComponentsRegistryTable.h" + +class PetTest : public GameDependenciesTest { +protected: + Entity* baseEntity; + PetComponent* petComponent; + CBITSTREAM + // uint32_t flags = 0; + void SetUp() override { + SetUpDependencies(); + + // Set up entity and pet component + baseEntity = new Entity(3050, GameDependenciesTest::info); + //CDComponentsRegistryTable* compRegistryTable = CDClientManager::Instance().GetTable(); + //uint32_t petComponentId = compRegistryTable->GetByIDAndType(baseEntity->GetLOT(), eReplicaComponentType::PET); + petComponent = baseEntity->AddComponent(1); + + // Initialize some values to be not default + petComponent->SetFlag(PetFlag::NONE); + petComponent->SetPetAiState(PetAiState::follow); + petComponent->SetAbility(ePetAbilityType::Invalid); + } + + void TearDown() override { + delete baseEntity; + TearDownDependencies(); + } +}; + +TEST_F(PetTest, PlacementNewAddComponentTest) { + ASSERT_NE(petComponent, nullptr); + baseEntity->AddComponent(); + ASSERT_NE(baseEntity->GetComponent(), nullptr); + ASSERT_EQ(petComponent->GetParent()->GetObjectID(), 15); + ASSERT_EQ(petComponent->HasFlag(), PetFlag::NONE); // UPDATE TO USE FLAGS + ASSERT_EQ(petComponent->GetPetAiState(), PetAiState::follow); + ASSERT_EQ(petComponent->GetAbility(), ePetAbilityType::Invalid); +} + +/** + * Test Construction of a PetComponent + */ +/*TEST_F(PetTest, PetComponent) { + petComponent->Serialize(&bitStream, true); + // Assert that the full number of bits are present + ASSERT_EQ(bitStream.GetNumberOfUnreadBits(), 748); + { + // Now read in the full serialized construction BitStream + bool optionStatusImmunityInfo{}; + uint32_t ImmuneToBasicAttackCount{}; + uint32_t ImmuneToDamageOverTimeCount{}; + uint32_t ImmuneToKnockbackCount{}; + uint32_t ImmuneToInterruptCount{}; + uint32_t ImmuneToSpeedCount{}; + uint32_t ImmuneToImaginationGainCount{}; + uint32_t ImmuneToImaginationLossCount{}; + uint32_t ImmuneToQuickbuildInterruptCount{}; + uint32_t ImmuneToPullToPointCount{}; + bool optionStatsInfo{}; + uint32_t currentHealth{}; + float maxHealth{}; + uint32_t currentArmor{}; + float maxArmor{}; + uint32_t currentImagination{}; + float maxImagination{}; + uint32_t damageAbsorptionPoints{}; + bool hasImmunity{}; + bool isGmImmune{}; + bool isShielded{}; + float actualMaxHealth{}; + float actualMaxArmor{}; + float actualMaxImagination{}; + uint32_t factionsSize{}; + std::vector factions{}; + bool isSmashable{}; + bool isDead{}; + bool isSmashed{}; + bool isModuleAssembly{}; + bool optionExplodeFactor{}; + float explodeFactor{}; + bool optionIsOnThreatList{}; + bool isThreatened{}; + bitStream.Read(optionStatusImmunityInfo); + bitStream.Read(ImmuneToBasicAttackCount); + bitStream.Read(ImmuneToDamageOverTimeCount); + bitStream.Read(ImmuneToKnockbackCount); + bitStream.Read(ImmuneToInterruptCount); + bitStream.Read(ImmuneToSpeedCount); + bitStream.Read(ImmuneToImaginationGainCount); + bitStream.Read(ImmuneToImaginationLossCount); + bitStream.Read(ImmuneToQuickbuildInterruptCount); + bitStream.Read(ImmuneToPullToPointCount); + bitStream.Read(optionStatsInfo); + bitStream.Read(currentHealth); + bitStream.Read(maxHealth); + bitStream.Read(currentArmor); + bitStream.Read(maxArmor); + bitStream.Read(currentImagination); + bitStream.Read(maxImagination); + bitStream.Read(damageAbsorptionPoints); + bitStream.Read(hasImmunity); + bitStream.Read(isGmImmune); + bitStream.Read(isShielded); + bitStream.Read(actualMaxHealth); + bitStream.Read(actualMaxArmor); + bitStream.Read(actualMaxImagination); + bitStream.Read(factionsSize); + for (uint32_t i = 0; i < factionsSize; i++) { + int32_t factionID{}; + bitStream.Read(factionID); + factions.push_back(factionID); + } + bitStream.Read(isSmashable); // This is an option later and also a flag at this spot + bitStream.Read(isDead); + bitStream.Read(isSmashed); + // if IsSmashable is true, read the next bits. + bitStream.Read(isModuleAssembly); + bitStream.Read(optionExplodeFactor); + bitStream.Read(explodeFactor); + + bitStream.Read(optionIsOnThreatList); + bitStream.Read(isThreatened); + EXPECT_EQ(optionStatusImmunityInfo, true); + EXPECT_EQ(ImmuneToBasicAttackCount, 0); + EXPECT_EQ(ImmuneToDamageOverTimeCount, 0); + EXPECT_EQ(ImmuneToKnockbackCount, 0); + EXPECT_EQ(ImmuneToInterruptCount, 0); + EXPECT_EQ(ImmuneToSpeedCount, 0); + EXPECT_EQ(ImmuneToImaginationGainCount, 0); + EXPECT_EQ(ImmuneToImaginationLossCount, 0); + EXPECT_EQ(ImmuneToQuickbuildInterruptCount, 0); + EXPECT_EQ(ImmuneToPullToPointCount, 0); + + EXPECT_EQ(optionStatsInfo, true); + EXPECT_EQ(currentHealth, 23); + EXPECT_EQ(maxHealth, 12345.0f); + EXPECT_EQ(currentArmor, 7); + EXPECT_EQ(maxArmor, 14.0f); + EXPECT_EQ(currentImagination, 6000); + EXPECT_EQ(maxImagination, 14000.0f); + EXPECT_EQ(damageAbsorptionPoints, 0.0f); + EXPECT_EQ(hasImmunity, false); + EXPECT_EQ(isGmImmune, false); + EXPECT_EQ(isShielded, false); + EXPECT_EQ(actualMaxHealth, 12345.0f); + EXPECT_EQ(actualMaxArmor, 14.0f); + EXPECT_EQ(actualMaxImagination, 14000.0f); + EXPECT_EQ(factionsSize, 2); + EXPECT_NE(std::find(factions.begin(), factions.end(), -1), factions.end()); + EXPECT_NE(std::find(factions.begin(), factions.end(), 6), factions.end()); + EXPECT_EQ(isSmashable, true); + EXPECT_EQ(isDead, false); + EXPECT_EQ(isSmashed, false); + EXPECT_EQ(isSmashable, true); // For the sake of readability with the struct viewers, we will test this twice since its used as an option here, but as a bool above. + EXPECT_EQ(isModuleAssembly, false); + EXPECT_EQ(optionExplodeFactor, true); + EXPECT_EQ(explodeFactor, 1.1f); + + EXPECT_EQ(optionIsOnThreatList, true); + EXPECT_EQ(isThreatened, false); + } + bitStream.Reset(); +}*/ + +/** + * Test serialization of a PetComponent + */ +/*TEST_F(PetTest, PEtComponentSerializeTest) { + bitStream.Reset(); + // Initialize some values to be not default so we can test a full serialization + destroyableComponent->SetMaxHealth(1233.0f); + + // Now we test a serialization for correctness. + destroyableComponent->Serialize(&bitStream, false); + ASSERT_EQ(bitStream.GetNumberOfUnreadBits(), 422); + { + // Now read in the full serialized BitStream + bool optionStatsInfo{}; + uint32_t currentHealth{}; + float maxHealth{}; + uint32_t currentArmor{}; + float maxArmor{}; + uint32_t currentImagination{}; + float maxImagination{}; + uint32_t damageAbsorptionPoints{}; + bool hasImmunity{}; + bool isGmImmune{}; + bool isShielded{}; + float actualMaxHealth{}; + float actualMaxArmor{}; + float actualMaxImagination{}; + uint32_t factionsSize{}; + std::vector factions{}; + bool isSmashable{}; + bool optionIsOnThreatList{}; + bitStream.Read(optionStatsInfo); + bitStream.Read(currentHealth); + bitStream.Read(maxHealth); + bitStream.Read(currentArmor); + bitStream.Read(maxArmor); + bitStream.Read(currentImagination); + bitStream.Read(maxImagination); + bitStream.Read(damageAbsorptionPoints); + bitStream.Read(hasImmunity); + bitStream.Read(isGmImmune); + bitStream.Read(isShielded); + bitStream.Read(actualMaxHealth); + bitStream.Read(actualMaxArmor); + bitStream.Read(actualMaxImagination); + bitStream.Read(factionsSize); + for (uint32_t i = 0; i < factionsSize; i++) { + int32_t factionID{}; + bitStream.Read(factionID); + factions.push_back(factionID); + } + bitStream.Read(isSmashable); + + bitStream.Read(optionIsOnThreatList); + + EXPECT_EQ(optionStatsInfo, true); + EXPECT_EQ(currentHealth, 23); + EXPECT_EQ(maxHealth, 1233.0f); + EXPECT_EQ(currentArmor, 7); + EXPECT_EQ(maxArmor, 14.0f); + EXPECT_EQ(currentImagination, 6000); + EXPECT_EQ(maxImagination, 14000.0f); + EXPECT_EQ(damageAbsorptionPoints, 0.0f); + EXPECT_EQ(hasImmunity, false); + EXPECT_EQ(isGmImmune, false); + EXPECT_EQ(isShielded, false); + EXPECT_EQ(actualMaxHealth, 1233.0f); + EXPECT_EQ(actualMaxArmor, 14.0f); + EXPECT_EQ(actualMaxImagination, 14000.0f); + EXPECT_EQ(factionsSize, 2); + EXPECT_NE(std::find(factions.begin(), factions.end(), -1), factions.end()); + EXPECT_NE(std::find(factions.begin(), factions.end(), 6), factions.end()); + EXPECT_EQ(isSmashable, true); + + EXPECT_EQ(optionIsOnThreatList, false); // Always zero for now on serialization + } +}*/ + +/** + * Test the Damage method of DestroyableComponent + */ +/*TEST_F(DestroyableTest, DestroyableComponentDamageTest) { + // Do some actions + destroyableComponent->SetMaxHealth(100.0f); + destroyableComponent->SetHealth(100); + destroyableComponent->SetMaxArmor(0.0f); + destroyableComponent->Damage(10, LWOOBJID_EMPTY); + // Check that we take damage + ASSERT_EQ(destroyableComponent->GetHealth(), 90); + // Check that if we have armor, we take the correct amount of damage + destroyableComponent->SetMaxArmor(10.0f); + destroyableComponent->SetArmor(5); + destroyableComponent->Damage(10, LWOOBJID_EMPTY); + ASSERT_EQ(destroyableComponent->GetHealth(), 85); + // Check that if we have damage absorption we take the correct damage + destroyableComponent->SetDamageToAbsorb(10); + destroyableComponent->Damage(9, LWOOBJID_EMPTY); + ASSERT_EQ(destroyableComponent->GetHealth(), 85); + ASSERT_EQ(destroyableComponent->GetDamageToAbsorb(), 1); + destroyableComponent->Damage(6, LWOOBJID_EMPTY); + ASSERT_EQ(destroyableComponent->GetHealth(), 80); + // Check that we take the correct reduced damage if we take reduced damage + destroyableComponent->SetDamageReduction(2); + destroyableComponent->Damage(7, LWOOBJID_EMPTY); + ASSERT_EQ(destroyableComponent->GetHealth(), 75); + destroyableComponent->Damage(2, LWOOBJID_EMPTY); + ASSERT_EQ(destroyableComponent->GetHealth(), 74); + ASSERT_EQ(destroyableComponent->GetDamageReduction(), 2); + destroyableComponent->SetDamageReduction(0); + // Check that blocking works + destroyableComponent->SetAttacksToBlock(1); + destroyableComponent->Damage(UINT32_MAX, LWOOBJID_EMPTY); + ASSERT_EQ(destroyableComponent->GetHealth(), 74); + destroyableComponent->Damage(4, LWOOBJID_EMPTY); + ASSERT_EQ(destroyableComponent->GetHealth(), 70); + // Check that immunity works + destroyableComponent->SetIsImmune(true); + destroyableComponent->Damage(UINT32_MAX, LWOOBJID_EMPTY); + ASSERT_EQ(destroyableComponent->GetHealth(), 70); + ASSERT_TRUE(destroyableComponent->IsImmune()); + destroyableComponent->SetIsImmune(false); + destroyableComponent->SetIsGMImmune(true); + destroyableComponent->Damage(UINT32_MAX, LWOOBJID_EMPTY); + ASSERT_EQ(destroyableComponent->GetHealth(), 70); + ASSERT_TRUE(destroyableComponent->IsImmune()); + destroyableComponent->SetIsGMImmune(false); + // Check knockback immunity + destroyableComponent->SetIsShielded(true); + ASSERT_TRUE(destroyableComponent->IsKnockbackImmune()); + // Finally deal enough damage to kill the Entity + destroyableComponent->Damage(71, LWOOBJID_EMPTY); + ASSERT_EQ(destroyableComponent->GetHealth(), 0); + // Now lets heal some stats + destroyableComponent->Heal(15); + ASSERT_EQ(destroyableComponent->GetHealth(), 15); + destroyableComponent->Heal(15000); + ASSERT_EQ(destroyableComponent->GetHealth(), 100); + destroyableComponent->Repair(10); + ASSERT_EQ(destroyableComponent->GetArmor(), 10); + destroyableComponent->Repair(15000); + ASSERT_EQ(destroyableComponent->GetArmor(), 10); + destroyableComponent->SetMaxImagination(100.0f); + destroyableComponent->SetImagination(0); + destroyableComponent->Imagine(99); + ASSERT_EQ(destroyableComponent->GetImagination(), 99); + destroyableComponent->Imagine(4); + ASSERT_EQ(destroyableComponent->GetImagination(), 100); +} + +TEST_F(DestroyableTest, DestroyableComponentFactionTest) { + ASSERT_TRUE(destroyableComponent->HasFaction(-1)); + ASSERT_TRUE(destroyableComponent->HasFaction(6)); +} + +TEST_F(DestroyableTest, DestroyableComponentValiditiyTest) { + auto* enemyEntity = new Entity(19, info); + enemyEntity->AddComponent()->AddFactionNoLookup(16); + destroyableComponent->AddEnemyFaction(16); + EXPECT_TRUE(destroyableComponent->IsEnemy(enemyEntity)); + EXPECT_FALSE(destroyableComponent->IsFriend(enemyEntity)); + delete enemyEntity; +} + +TEST_F(DestroyableTest, DestroyableComponentImmunityTest) { + // assert to show that they are empty + ASSERT_FALSE(destroyableComponent->GetImmuneToBasicAttack()); + ASSERT_FALSE(destroyableComponent->GetImmuneToDamageOverTime()); + ASSERT_FALSE(destroyableComponent->GetImmuneToKnockback()); + ASSERT_FALSE(destroyableComponent->GetImmuneToInterrupt()); + ASSERT_FALSE(destroyableComponent->GetImmuneToSpeed()); + ASSERT_FALSE(destroyableComponent->GetImmuneToImaginationGain()); + ASSERT_FALSE(destroyableComponent->GetImmuneToImaginationLoss()); + ASSERT_FALSE(destroyableComponent->GetImmuneToQuickbuildInterrupt()); + ASSERT_FALSE(destroyableComponent->GetImmuneToPullToPoint()); + // set them all to true (count 1) and check + destroyableComponent->SetStatusImmunity(eStateChangeType::PUSH, true, true, true, true, true, true, true, true, true); + ASSERT_TRUE(destroyableComponent->GetImmuneToBasicAttack()); + ASSERT_TRUE(destroyableComponent->GetImmuneToDamageOverTime()); + ASSERT_TRUE(destroyableComponent->GetImmuneToKnockback()); + ASSERT_TRUE(destroyableComponent->GetImmuneToInterrupt()); + ASSERT_TRUE(destroyableComponent->GetImmuneToSpeed()); + ASSERT_TRUE(destroyableComponent->GetImmuneToImaginationGain()); + ASSERT_TRUE(destroyableComponent->GetImmuneToImaginationLoss()); + ASSERT_TRUE(destroyableComponent->GetImmuneToQuickbuildInterrupt()); + ASSERT_TRUE(destroyableComponent->GetImmuneToPullToPoint()); + + // remove them to check that they get removed properly + destroyableComponent->SetStatusImmunity(eStateChangeType::POP, true, true, true, true, true, true, true, true, true); + ASSERT_FALSE(destroyableComponent->GetImmuneToBasicAttack()); + ASSERT_FALSE(destroyableComponent->GetImmuneToDamageOverTime()); + ASSERT_FALSE(destroyableComponent->GetImmuneToKnockback()); + ASSERT_FALSE(destroyableComponent->GetImmuneToInterrupt()); + ASSERT_FALSE(destroyableComponent->GetImmuneToSpeed()); + ASSERT_FALSE(destroyableComponent->GetImmuneToImaginationGain()); + ASSERT_FALSE(destroyableComponent->GetImmuneToImaginationLoss()); + ASSERT_FALSE(destroyableComponent->GetImmuneToQuickbuildInterrupt()); + ASSERT_FALSE(destroyableComponent->GetImmuneToPullToPoint()); + + // should not crash to remove them again + destroyableComponent->SetStatusImmunity(eStateChangeType::POP, true, true, true, true, true, true, true, true, true); + ASSERT_FALSE(destroyableComponent->GetImmuneToBasicAttack()); + ASSERT_FALSE(destroyableComponent->GetImmuneToDamageOverTime()); + ASSERT_FALSE(destroyableComponent->GetImmuneToKnockback()); + ASSERT_FALSE(destroyableComponent->GetImmuneToInterrupt()); + ASSERT_FALSE(destroyableComponent->GetImmuneToSpeed()); + ASSERT_FALSE(destroyableComponent->GetImmuneToImaginationGain()); + ASSERT_FALSE(destroyableComponent->GetImmuneToImaginationLoss()); + ASSERT_FALSE(destroyableComponent->GetImmuneToQuickbuildInterrupt()); + ASSERT_FALSE(destroyableComponent->GetImmuneToPullToPoint()); + + // just do one + destroyableComponent->SetStatusImmunity(eStateChangeType::PUSH, true); + ASSERT_TRUE(destroyableComponent->GetImmuneToBasicAttack()); + ASSERT_FALSE(destroyableComponent->GetImmuneToDamageOverTime()); + ASSERT_FALSE(destroyableComponent->GetImmuneToKnockback()); + ASSERT_FALSE(destroyableComponent->GetImmuneToInterrupt()); + ASSERT_FALSE(destroyableComponent->GetImmuneToSpeed()); + ASSERT_FALSE(destroyableComponent->GetImmuneToImaginationGain()); + ASSERT_FALSE(destroyableComponent->GetImmuneToImaginationLoss()); + ASSERT_FALSE(destroyableComponent->GetImmuneToQuickbuildInterrupt()); + ASSERT_FALSE(destroyableComponent->GetImmuneToPullToPoint()); + // now stack it to 2 on basic attack + destroyableComponent->SetStatusImmunity(eStateChangeType::PUSH, true); + ASSERT_TRUE(destroyableComponent->GetImmuneToBasicAttack()); + ASSERT_FALSE(destroyableComponent->GetImmuneToDamageOverTime()); + ASSERT_FALSE(destroyableComponent->GetImmuneToKnockback()); + ASSERT_FALSE(destroyableComponent->GetImmuneToInterrupt()); + ASSERT_FALSE(destroyableComponent->GetImmuneToSpeed()); + ASSERT_FALSE(destroyableComponent->GetImmuneToImaginationGain()); + ASSERT_FALSE(destroyableComponent->GetImmuneToImaginationLoss()); + ASSERT_FALSE(destroyableComponent->GetImmuneToQuickbuildInterrupt()); + ASSERT_FALSE(destroyableComponent->GetImmuneToPullToPoint()); + // remove one and still shoudl be true + destroyableComponent->SetStatusImmunity(eStateChangeType::POP, true); + ASSERT_TRUE(destroyableComponent->GetImmuneToBasicAttack()); + ASSERT_FALSE(destroyableComponent->GetImmuneToDamageOverTime()); + ASSERT_FALSE(destroyableComponent->GetImmuneToKnockback()); + ASSERT_FALSE(destroyableComponent->GetImmuneToInterrupt()); + ASSERT_FALSE(destroyableComponent->GetImmuneToSpeed()); + ASSERT_FALSE(destroyableComponent->GetImmuneToImaginationGain()); + ASSERT_FALSE(destroyableComponent->GetImmuneToImaginationLoss()); + ASSERT_FALSE(destroyableComponent->GetImmuneToQuickbuildInterrupt()); + ASSERT_FALSE(destroyableComponent->GetImmuneToPullToPoint()); + // go back to 0 + destroyableComponent->SetStatusImmunity(eStateChangeType::POP, true); + ASSERT_FALSE(destroyableComponent->GetImmuneToBasicAttack()); + ASSERT_FALSE(destroyableComponent->GetImmuneToDamageOverTime()); + ASSERT_FALSE(destroyableComponent->GetImmuneToKnockback()); + ASSERT_FALSE(destroyableComponent->GetImmuneToInterrupt()); + ASSERT_FALSE(destroyableComponent->GetImmuneToSpeed()); + ASSERT_FALSE(destroyableComponent->GetImmuneToImaginationGain()); + ASSERT_FALSE(destroyableComponent->GetImmuneToImaginationLoss()); + ASSERT_FALSE(destroyableComponent->GetImmuneToQuickbuildInterrupt()); + ASSERT_FALSE(destroyableComponent->GetImmuneToPullToPoint()); + // check individual ones now + destroyableComponent->SetStatusImmunity(eStateChangeType::PUSH, true, false, false, false, false, false, false, false, false); + ASSERT_TRUE(destroyableComponent->GetImmuneToBasicAttack()); + ASSERT_FALSE(destroyableComponent->GetImmuneToDamageOverTime()); + ASSERT_FALSE(destroyableComponent->GetImmuneToKnockback()); + ASSERT_FALSE(destroyableComponent->GetImmuneToInterrupt()); + ASSERT_FALSE(destroyableComponent->GetImmuneToSpeed()); + ASSERT_FALSE(destroyableComponent->GetImmuneToImaginationGain()); + ASSERT_FALSE(destroyableComponent->GetImmuneToImaginationLoss()); + ASSERT_FALSE(destroyableComponent->GetImmuneToQuickbuildInterrupt()); + ASSERT_FALSE(destroyableComponent->GetImmuneToPullToPoint()); + destroyableComponent->SetStatusImmunity(eStateChangeType::POP, true, false, false, false, false, false, false, false, false); + + destroyableComponent->SetStatusImmunity(eStateChangeType::PUSH, false, true, false, false, false, false, false, false, false); + ASSERT_FALSE(destroyableComponent->GetImmuneToBasicAttack()); + ASSERT_TRUE(destroyableComponent->GetImmuneToDamageOverTime()); + ASSERT_FALSE(destroyableComponent->GetImmuneToKnockback()); + ASSERT_FALSE(destroyableComponent->GetImmuneToInterrupt()); + ASSERT_FALSE(destroyableComponent->GetImmuneToSpeed()); + ASSERT_FALSE(destroyableComponent->GetImmuneToImaginationGain()); + ASSERT_FALSE(destroyableComponent->GetImmuneToImaginationLoss()); + ASSERT_FALSE(destroyableComponent->GetImmuneToQuickbuildInterrupt()); + ASSERT_FALSE(destroyableComponent->GetImmuneToPullToPoint()); + destroyableComponent->SetStatusImmunity(eStateChangeType::POP, false, true, false, false, false, false, false, false, false); + + destroyableComponent->SetStatusImmunity(eStateChangeType::PUSH, false, false, true, false, false, false, false, false, false); + ASSERT_FALSE(destroyableComponent->GetImmuneToBasicAttack()); + ASSERT_FALSE(destroyableComponent->GetImmuneToDamageOverTime()); + ASSERT_TRUE(destroyableComponent->GetImmuneToKnockback()); + ASSERT_FALSE(destroyableComponent->GetImmuneToInterrupt()); + ASSERT_FALSE(destroyableComponent->GetImmuneToSpeed()); + ASSERT_FALSE(destroyableComponent->GetImmuneToImaginationGain()); + ASSERT_FALSE(destroyableComponent->GetImmuneToImaginationLoss()); + ASSERT_FALSE(destroyableComponent->GetImmuneToQuickbuildInterrupt()); + ASSERT_FALSE(destroyableComponent->GetImmuneToPullToPoint()); + destroyableComponent->SetStatusImmunity(eStateChangeType::POP, false, false, true, false, false, false, false, false, false); + + destroyableComponent->SetStatusImmunity(eStateChangeType::PUSH, false, false, false, true, false, false, false, false, false); + ASSERT_FALSE(destroyableComponent->GetImmuneToBasicAttack()); + ASSERT_FALSE(destroyableComponent->GetImmuneToDamageOverTime()); + ASSERT_FALSE(destroyableComponent->GetImmuneToKnockback()); + ASSERT_TRUE(destroyableComponent->GetImmuneToInterrupt()); + ASSERT_FALSE(destroyableComponent->GetImmuneToSpeed()); + ASSERT_FALSE(destroyableComponent->GetImmuneToImaginationGain()); + ASSERT_FALSE(destroyableComponent->GetImmuneToImaginationLoss()); + ASSERT_FALSE(destroyableComponent->GetImmuneToQuickbuildInterrupt()); + ASSERT_FALSE(destroyableComponent->GetImmuneToPullToPoint()); + destroyableComponent->SetStatusImmunity(eStateChangeType::POP, false, false, false, true, false, false, false, false, false); + + destroyableComponent->SetStatusImmunity(eStateChangeType::PUSH, false, false, false, false, true, false, false, false, false); + ASSERT_FALSE(destroyableComponent->GetImmuneToBasicAttack()); + ASSERT_FALSE(destroyableComponent->GetImmuneToDamageOverTime()); + ASSERT_FALSE(destroyableComponent->GetImmuneToKnockback()); + ASSERT_FALSE(destroyableComponent->GetImmuneToInterrupt()); + ASSERT_TRUE(destroyableComponent->GetImmuneToSpeed()); + ASSERT_FALSE(destroyableComponent->GetImmuneToImaginationGain()); + ASSERT_FALSE(destroyableComponent->GetImmuneToImaginationLoss()); + ASSERT_FALSE(destroyableComponent->GetImmuneToQuickbuildInterrupt()); + ASSERT_FALSE(destroyableComponent->GetImmuneToPullToPoint()); + destroyableComponent->SetStatusImmunity(eStateChangeType::POP, false, false, false, false, true, false, false, false, false); + + destroyableComponent->SetStatusImmunity(eStateChangeType::PUSH, false, false, false, false, false, true, false, false, false); + ASSERT_FALSE(destroyableComponent->GetImmuneToBasicAttack()); + ASSERT_FALSE(destroyableComponent->GetImmuneToDamageOverTime()); + ASSERT_FALSE(destroyableComponent->GetImmuneToKnockback()); + ASSERT_FALSE(destroyableComponent->GetImmuneToInterrupt()); + ASSERT_FALSE(destroyableComponent->GetImmuneToSpeed()); + ASSERT_TRUE(destroyableComponent->GetImmuneToImaginationGain()); + ASSERT_FALSE(destroyableComponent->GetImmuneToImaginationLoss()); + ASSERT_FALSE(destroyableComponent->GetImmuneToQuickbuildInterrupt()); + ASSERT_FALSE(destroyableComponent->GetImmuneToPullToPoint()); + destroyableComponent->SetStatusImmunity(eStateChangeType::POP, false, false, false, false, false, true, false, false, false); + + destroyableComponent->SetStatusImmunity(eStateChangeType::PUSH, false, false, false, false, false, false, true, false, false); + ASSERT_FALSE(destroyableComponent->GetImmuneToBasicAttack()); + ASSERT_FALSE(destroyableComponent->GetImmuneToDamageOverTime()); + ASSERT_FALSE(destroyableComponent->GetImmuneToKnockback()); + ASSERT_FALSE(destroyableComponent->GetImmuneToInterrupt()); + ASSERT_FALSE(destroyableComponent->GetImmuneToSpeed()); + ASSERT_FALSE(destroyableComponent->GetImmuneToImaginationGain()); + ASSERT_TRUE(destroyableComponent->GetImmuneToImaginationLoss()); + ASSERT_FALSE(destroyableComponent->GetImmuneToQuickbuildInterrupt()); + ASSERT_FALSE(destroyableComponent->GetImmuneToPullToPoint()); + destroyableComponent->SetStatusImmunity(eStateChangeType::POP, false, false, false, false, false, false, true, false, false); + + + destroyableComponent->SetStatusImmunity(eStateChangeType::PUSH, false, false, false, false, false, false, false, true, false); + ASSERT_FALSE(destroyableComponent->GetImmuneToBasicAttack()); + ASSERT_FALSE(destroyableComponent->GetImmuneToDamageOverTime()); + ASSERT_FALSE(destroyableComponent->GetImmuneToKnockback()); + ASSERT_FALSE(destroyableComponent->GetImmuneToInterrupt()); + ASSERT_FALSE(destroyableComponent->GetImmuneToSpeed()); + ASSERT_FALSE(destroyableComponent->GetImmuneToImaginationGain()); + ASSERT_FALSE(destroyableComponent->GetImmuneToImaginationLoss()); + ASSERT_TRUE(destroyableComponent->GetImmuneToQuickbuildInterrupt()); + ASSERT_FALSE(destroyableComponent->GetImmuneToPullToPoint()); + destroyableComponent->SetStatusImmunity(eStateChangeType::POP, false, false, false, false, false, false, false, true, false); + + + destroyableComponent->SetStatusImmunity(eStateChangeType::PUSH, false, false, false, false, false, false, false, false, true); + ASSERT_FALSE(destroyableComponent->GetImmuneToBasicAttack()); + ASSERT_FALSE(destroyableComponent->GetImmuneToDamageOverTime()); + ASSERT_FALSE(destroyableComponent->GetImmuneToKnockback()); + ASSERT_FALSE(destroyableComponent->GetImmuneToInterrupt()); + ASSERT_FALSE(destroyableComponent->GetImmuneToSpeed()); + ASSERT_FALSE(destroyableComponent->GetImmuneToImaginationGain()); + ASSERT_FALSE(destroyableComponent->GetImmuneToImaginationLoss()); + ASSERT_FALSE(destroyableComponent->GetImmuneToQuickbuildInterrupt()); + ASSERT_TRUE(destroyableComponent->GetImmuneToPullToPoint()); + destroyableComponent->SetStatusImmunity(eStateChangeType::POP, false, false, false, false, false, false, false, false, true); + +}*/ + +/** + * Test the Damage cooldown timer of DestroyableComponent + */ +/*TEST_F(DestroyableTest, DestroyableComponentDamageCooldownTest) { + // Test the damage immune timer state (anything above 0.0f) + destroyableComponent->SetDamageCooldownTimer(1.0f); + EXPECT_FLOAT_EQ(destroyableComponent->GetDamageCooldownTimer(), 1.0f); + ASSERT_TRUE(destroyableComponent->IsCooldownImmune()); + + // Test that the Update() function correctly decrements the damage cooldown timer + destroyableComponent->Update(0.5f); + EXPECT_FLOAT_EQ(destroyableComponent->GetDamageCooldownTimer(), 0.5f); + ASSERT_TRUE(destroyableComponent->IsCooldownImmune()); + + // Test the non damage immune timer state (anything below or equal to 0.0f) + destroyableComponent->SetDamageCooldownTimer(0.0f); + EXPECT_FLOAT_EQ(destroyableComponent->GetDamageCooldownTimer(), 0.0f); + ASSERT_FALSE(destroyableComponent->IsCooldownImmune()); +}*/ + +TEST_F(PetTest, PetComponentFlagTest) { + // Test setting and reading single flags + petComponent->SetFlag(PetFlag::NOT_WAITING); + ASSERT_TRUE(petComponent->HasFlag(PetFlag::NOT_WAITING)); + ASSERT_FALSE(petComponent->HasFlag(PetFlag::NOT_WAITING)); + + // Test +} From 054371dd0132a104bf121e036611863935db942c Mon Sep 17 00:00:00 2001 From: jadebenn Date: Fri, 15 Dec 2023 05:34:38 -0600 Subject: [PATCH 36/73] added new pet bitflag system --- dGame/dComponents/PetComponent.cpp | 11 +- dGame/dComponents/PetComponent.h | 48 +- dZoneManager/WorldConfig.h | 2 +- dZoneManager/dZoneManager.h | 3 + tests/dGameTests/GameDependencies.h | 8 + .../dComponentsTests/PetComponentTests.cpp | 559 +----------------- 6 files changed, 83 insertions(+), 548 deletions(-) diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index a7937207a..08360380a 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -103,10 +103,13 @@ PetComponent::PetComponent(Entity* parent, uint32_t componentId): Component(pare SetPreconditions(checkPreconditions); } - // Get pet information from the CDClient + //LoadDataFromTemplate(); // TODO: Figure out how to load this with the tests (DarkflameServer/dDatabase/CDClientDatabase/CDClientTables/) +} + +void PetComponent::LoadDataFromTemplate() { auto query = CDClientDatabase::CreatePreppedStmt( "SELECT walkSpeed, runSpeed, sprintSpeed, imaginationDrainRate FROM PetComponent WHERE id = ?;"); - query.bind(1, static_cast(componentId)); + query.bind(1, static_cast(m_ComponentId)); auto result = query.execQuery(); @@ -116,8 +119,6 @@ PetComponent::PetComponent(Entity* parent, uint32_t componentId): Component(pare m_SprintSpeed = result.getFloatField(2, 10.0f); imaginationDrainRate = result.getFloatField(3, 60.0f); } - - result.finalize(); } void PetComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) { @@ -1053,7 +1054,7 @@ void PetComponent::Activate(Item* item, bool registerPet, bool fromTaming) { // auto* owner = GetOwner(); if (owner == nullptr) return; - SetStatus(1 << PetFlag::PLAY_SPAWN_ANIM); + SetStatus(1 << PetFlag::SPAWNING); auto databaseData = inventoryComponent->GetDatabasePet(m_DatabaseId); diff --git a/dGame/dComponents/PetComponent.h b/dGame/dComponents/PetComponent.h index 2c11646de..bb36b9241 100644 --- a/dGame/dComponents/PetComponent.h +++ b/dGame/dComponents/PetComponent.h @@ -36,7 +36,7 @@ enum PetFlag : uint32_t { NONE, BEING_TAMED = 4, NOT_WAITING = 5, - PLAY_SPAWN_ANIM = 7, + SPAWNING = 7, TAMEABLE = 8 }; @@ -68,6 +68,16 @@ class PetComponent : public Component { PetComponent(Entity* parentEntity, uint32_t componentId); ~PetComponent() override; + /** + * Loads pet data from CDClient + */ + void LoadDataFromTemplate(); //TODO: Move + + /** + * Serializes the pet + * @param outBitStream The output bitstream + * @param bIsInitialUpdate Boolean value of whether this is the initial update + */ void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override; /** @@ -82,23 +92,35 @@ class PetComponent : public Component { PetAiState GetPetAiState() { return m_State; }; /** - * Template function for setting pet flags + * Sets one or more pet flags */ - template - void SetFlag(T... flag) { - //m_Flags |= (uint32_t)flag; - m_Flags |= ((uint32_t)flag | ...);//(static_cast(flag) | ...); - //T::operator|(m_Flags, flag...); + template + void SetFlag(varArg... flag) { + const auto initFlags = m_Flags; + m_Flags |= (static_cast(1 << flag) | ...); + LOG_DEBUG("SetFlag: %d + %d = %d", initFlags, 1 << (flag | ...), m_Flags); } /** - * Template function for getting pet flags + * Unsets one or more pet flags + */ + template + void UnsetFlag(varArg... flag) { + const auto initFlags = m_Flags; + m_Flags &= ~(static_cast(1 << flag) | ...); + LOG_DEBUG("UnsetFlag: %d - %d = %d", initFlags, 1 << (flag | ...), m_Flags); + } // THIS IS PROBLEMATIC + + /** + * Gets one or more pet flags */ - template - const bool HasFlag(T... flag) { - //return (m_Flags & (u_int32_t)flag) == (u_int32_t)flag; - //return T::operator&(m_Flags, flag...) == T::operator&(flag...); - return true; + template + const bool HasFlag(varArg... flag) { + const auto lside = (m_Flags & (static_cast(1 << flag) | ...)); + const auto rside = (static_cast(1 << flag) | ...); + LOG_DEBUG("HasFlag: %d == %d", lside, rside); + return lside == rside; + //return (m_Flags & (static_cast(flag) | ...)) == (static_cast(flag) | ...); } void Update(float deltaTime) override; diff --git a/dZoneManager/WorldConfig.h b/dZoneManager/WorldConfig.h index a98433a1e..5c78ce1b1 100644 --- a/dZoneManager/WorldConfig.h +++ b/dZoneManager/WorldConfig.h @@ -5,7 +5,7 @@ #include struct WorldConfig { - int32_t worldConfigID{}; //! Primary key for WorlcConfig table + int32_t worldConfigID{}; //! Primary key for WorldConfig table float peGravityValue{}; //! Unknown float peBroadphaseWorldSize{}; //! Unknown float peGameObjScaleFactor{}; //! Unknown diff --git a/dZoneManager/dZoneManager.h b/dZoneManager/dZoneManager.h index 21ef013ad..a16be2c82 100644 --- a/dZoneManager/dZoneManager.h +++ b/dZoneManager/dZoneManager.h @@ -54,6 +54,9 @@ class dZoneManager { return m_WorldConfig; }; + // Override world config (for debugging purposes) + void SetWorldConfig(WorldConfig* worldConfig) { m_WorldConfig = worldConfig; } + private: /** * The starting unique mission ID. diff --git a/tests/dGameTests/GameDependencies.h b/tests/dGameTests/GameDependencies.h index 096dcb13e..92dafc0c8 100644 --- a/tests/dGameTests/GameDependencies.h +++ b/tests/dGameTests/GameDependencies.h @@ -6,7 +6,9 @@ #include "dServer.h" #include "EntityInfo.h" #include "EntityManager.h" +#include "dZoneManager.h" #include "dConfig.h" +#include "WorldConfig.h" #include class dZoneManager; @@ -33,6 +35,12 @@ class GameDependenciesTest : public ::testing::Test { Game::server = new dServerMock(); Game::config = new dConfig("worldconfig.ini"); Game::entityManager = new EntityManager(); + + // Set up zone manager + Game::zoneManager = new dZoneManager(); + auto worldConfig = new WorldConfig(); + worldConfig->petFollowRadius = 10.0f; + Game::zoneManager->SetWorldConfig(worldConfig); } void TearDownDependencies() { diff --git a/tests/dGameTests/dComponentsTests/PetComponentTests.cpp b/tests/dGameTests/dComponentsTests/PetComponentTests.cpp index 56fe409f0..2943ae0a1 100644 --- a/tests/dGameTests/dComponentsTests/PetComponentTests.cpp +++ b/tests/dGameTests/dComponentsTests/PetComponentTests.cpp @@ -8,28 +8,22 @@ #include "ePetAbilityType.h" #include "eStateChangeType.h" -// Table includes -#include "CDClientManager.h" -#include "CDComponentsRegistryTable.h" - class PetTest : public GameDependenciesTest { protected: Entity* baseEntity; PetComponent* petComponent; CBITSTREAM - // uint32_t flags = 0; + void SetUp() override { SetUpDependencies(); // Set up entity and pet component - baseEntity = new Entity(3050, GameDependenciesTest::info); - //CDComponentsRegistryTable* compRegistryTable = CDClientManager::Instance().GetTable(); - //uint32_t petComponentId = compRegistryTable->GetByIDAndType(baseEntity->GetLOT(), eReplicaComponentType::PET); + baseEntity = new Entity(15, GameDependenciesTest::info); petComponent = baseEntity->AddComponent(1); // Initialize some values to be not default - petComponent->SetFlag(PetFlag::NONE); - petComponent->SetPetAiState(PetAiState::follow); + petComponent->SetStatus(0); + petComponent->SetPetAiState(PetAiState::spawn); petComponent->SetAbility(ePetAbilityType::Invalid); } @@ -41,531 +35,38 @@ class PetTest : public GameDependenciesTest { TEST_F(PetTest, PlacementNewAddComponentTest) { ASSERT_NE(petComponent, nullptr); - baseEntity->AddComponent(); + baseEntity->AddComponent(1); ASSERT_NE(baseEntity->GetComponent(), nullptr); ASSERT_EQ(petComponent->GetParent()->GetObjectID(), 15); - ASSERT_EQ(petComponent->HasFlag(), PetFlag::NONE); // UPDATE TO USE FLAGS - ASSERT_EQ(petComponent->GetPetAiState(), PetAiState::follow); + ASSERT_EQ(petComponent->GetStatus(), PetFlag::NONE); + ASSERT_EQ(petComponent->GetPetAiState(), PetAiState::spawn); ASSERT_EQ(petComponent->GetAbility(), ePetAbilityType::Invalid); } /** - * Test Construction of a PetComponent - */ -/*TEST_F(PetTest, PetComponent) { - petComponent->Serialize(&bitStream, true); - // Assert that the full number of bits are present - ASSERT_EQ(bitStream.GetNumberOfUnreadBits(), 748); - { - // Now read in the full serialized construction BitStream - bool optionStatusImmunityInfo{}; - uint32_t ImmuneToBasicAttackCount{}; - uint32_t ImmuneToDamageOverTimeCount{}; - uint32_t ImmuneToKnockbackCount{}; - uint32_t ImmuneToInterruptCount{}; - uint32_t ImmuneToSpeedCount{}; - uint32_t ImmuneToImaginationGainCount{}; - uint32_t ImmuneToImaginationLossCount{}; - uint32_t ImmuneToQuickbuildInterruptCount{}; - uint32_t ImmuneToPullToPointCount{}; - bool optionStatsInfo{}; - uint32_t currentHealth{}; - float maxHealth{}; - uint32_t currentArmor{}; - float maxArmor{}; - uint32_t currentImagination{}; - float maxImagination{}; - uint32_t damageAbsorptionPoints{}; - bool hasImmunity{}; - bool isGmImmune{}; - bool isShielded{}; - float actualMaxHealth{}; - float actualMaxArmor{}; - float actualMaxImagination{}; - uint32_t factionsSize{}; - std::vector factions{}; - bool isSmashable{}; - bool isDead{}; - bool isSmashed{}; - bool isModuleAssembly{}; - bool optionExplodeFactor{}; - float explodeFactor{}; - bool optionIsOnThreatList{}; - bool isThreatened{}; - bitStream.Read(optionStatusImmunityInfo); - bitStream.Read(ImmuneToBasicAttackCount); - bitStream.Read(ImmuneToDamageOverTimeCount); - bitStream.Read(ImmuneToKnockbackCount); - bitStream.Read(ImmuneToInterruptCount); - bitStream.Read(ImmuneToSpeedCount); - bitStream.Read(ImmuneToImaginationGainCount); - bitStream.Read(ImmuneToImaginationLossCount); - bitStream.Read(ImmuneToQuickbuildInterruptCount); - bitStream.Read(ImmuneToPullToPointCount); - bitStream.Read(optionStatsInfo); - bitStream.Read(currentHealth); - bitStream.Read(maxHealth); - bitStream.Read(currentArmor); - bitStream.Read(maxArmor); - bitStream.Read(currentImagination); - bitStream.Read(maxImagination); - bitStream.Read(damageAbsorptionPoints); - bitStream.Read(hasImmunity); - bitStream.Read(isGmImmune); - bitStream.Read(isShielded); - bitStream.Read(actualMaxHealth); - bitStream.Read(actualMaxArmor); - bitStream.Read(actualMaxImagination); - bitStream.Read(factionsSize); - for (uint32_t i = 0; i < factionsSize; i++) { - int32_t factionID{}; - bitStream.Read(factionID); - factions.push_back(factionID); - } - bitStream.Read(isSmashable); // This is an option later and also a flag at this spot - bitStream.Read(isDead); - bitStream.Read(isSmashed); - // if IsSmashable is true, read the next bits. - bitStream.Read(isModuleAssembly); - bitStream.Read(optionExplodeFactor); - bitStream.Read(explodeFactor); - - bitStream.Read(optionIsOnThreatList); - bitStream.Read(isThreatened); - EXPECT_EQ(optionStatusImmunityInfo, true); - EXPECT_EQ(ImmuneToBasicAttackCount, 0); - EXPECT_EQ(ImmuneToDamageOverTimeCount, 0); - EXPECT_EQ(ImmuneToKnockbackCount, 0); - EXPECT_EQ(ImmuneToInterruptCount, 0); - EXPECT_EQ(ImmuneToSpeedCount, 0); - EXPECT_EQ(ImmuneToImaginationGainCount, 0); - EXPECT_EQ(ImmuneToImaginationLossCount, 0); - EXPECT_EQ(ImmuneToQuickbuildInterruptCount, 0); - EXPECT_EQ(ImmuneToPullToPointCount, 0); - - EXPECT_EQ(optionStatsInfo, true); - EXPECT_EQ(currentHealth, 23); - EXPECT_EQ(maxHealth, 12345.0f); - EXPECT_EQ(currentArmor, 7); - EXPECT_EQ(maxArmor, 14.0f); - EXPECT_EQ(currentImagination, 6000); - EXPECT_EQ(maxImagination, 14000.0f); - EXPECT_EQ(damageAbsorptionPoints, 0.0f); - EXPECT_EQ(hasImmunity, false); - EXPECT_EQ(isGmImmune, false); - EXPECT_EQ(isShielded, false); - EXPECT_EQ(actualMaxHealth, 12345.0f); - EXPECT_EQ(actualMaxArmor, 14.0f); - EXPECT_EQ(actualMaxImagination, 14000.0f); - EXPECT_EQ(factionsSize, 2); - EXPECT_NE(std::find(factions.begin(), factions.end(), -1), factions.end()); - EXPECT_NE(std::find(factions.begin(), factions.end(), 6), factions.end()); - EXPECT_EQ(isSmashable, true); - EXPECT_EQ(isDead, false); - EXPECT_EQ(isSmashed, false); - EXPECT_EQ(isSmashable, true); // For the sake of readability with the struct viewers, we will test this twice since its used as an option here, but as a bool above. - EXPECT_EQ(isModuleAssembly, false); - EXPECT_EQ(optionExplodeFactor, true); - EXPECT_EQ(explodeFactor, 1.1f); - - EXPECT_EQ(optionIsOnThreatList, true); - EXPECT_EQ(isThreatened, false); - } - bitStream.Reset(); -}*/ - -/** - * Test serialization of a PetComponent - */ -/*TEST_F(PetTest, PEtComponentSerializeTest) { - bitStream.Reset(); - // Initialize some values to be not default so we can test a full serialization - destroyableComponent->SetMaxHealth(1233.0f); - - // Now we test a serialization for correctness. - destroyableComponent->Serialize(&bitStream, false); - ASSERT_EQ(bitStream.GetNumberOfUnreadBits(), 422); - { - // Now read in the full serialized BitStream - bool optionStatsInfo{}; - uint32_t currentHealth{}; - float maxHealth{}; - uint32_t currentArmor{}; - float maxArmor{}; - uint32_t currentImagination{}; - float maxImagination{}; - uint32_t damageAbsorptionPoints{}; - bool hasImmunity{}; - bool isGmImmune{}; - bool isShielded{}; - float actualMaxHealth{}; - float actualMaxArmor{}; - float actualMaxImagination{}; - uint32_t factionsSize{}; - std::vector factions{}; - bool isSmashable{}; - bool optionIsOnThreatList{}; - bitStream.Read(optionStatsInfo); - bitStream.Read(currentHealth); - bitStream.Read(maxHealth); - bitStream.Read(currentArmor); - bitStream.Read(maxArmor); - bitStream.Read(currentImagination); - bitStream.Read(maxImagination); - bitStream.Read(damageAbsorptionPoints); - bitStream.Read(hasImmunity); - bitStream.Read(isGmImmune); - bitStream.Read(isShielded); - bitStream.Read(actualMaxHealth); - bitStream.Read(actualMaxArmor); - bitStream.Read(actualMaxImagination); - bitStream.Read(factionsSize); - for (uint32_t i = 0; i < factionsSize; i++) { - int32_t factionID{}; - bitStream.Read(factionID); - factions.push_back(factionID); - } - bitStream.Read(isSmashable); - - bitStream.Read(optionIsOnThreatList); - - EXPECT_EQ(optionStatsInfo, true); - EXPECT_EQ(currentHealth, 23); - EXPECT_EQ(maxHealth, 1233.0f); - EXPECT_EQ(currentArmor, 7); - EXPECT_EQ(maxArmor, 14.0f); - EXPECT_EQ(currentImagination, 6000); - EXPECT_EQ(maxImagination, 14000.0f); - EXPECT_EQ(damageAbsorptionPoints, 0.0f); - EXPECT_EQ(hasImmunity, false); - EXPECT_EQ(isGmImmune, false); - EXPECT_EQ(isShielded, false); - EXPECT_EQ(actualMaxHealth, 1233.0f); - EXPECT_EQ(actualMaxArmor, 14.0f); - EXPECT_EQ(actualMaxImagination, 14000.0f); - EXPECT_EQ(factionsSize, 2); - EXPECT_NE(std::find(factions.begin(), factions.end(), -1), factions.end()); - EXPECT_NE(std::find(factions.begin(), factions.end(), 6), factions.end()); - EXPECT_EQ(isSmashable, true); - - EXPECT_EQ(optionIsOnThreatList, false); // Always zero for now on serialization - } -}*/ - -/** - * Test the Damage method of DestroyableComponent - */ -/*TEST_F(DestroyableTest, DestroyableComponentDamageTest) { - // Do some actions - destroyableComponent->SetMaxHealth(100.0f); - destroyableComponent->SetHealth(100); - destroyableComponent->SetMaxArmor(0.0f); - destroyableComponent->Damage(10, LWOOBJID_EMPTY); - // Check that we take damage - ASSERT_EQ(destroyableComponent->GetHealth(), 90); - // Check that if we have armor, we take the correct amount of damage - destroyableComponent->SetMaxArmor(10.0f); - destroyableComponent->SetArmor(5); - destroyableComponent->Damage(10, LWOOBJID_EMPTY); - ASSERT_EQ(destroyableComponent->GetHealth(), 85); - // Check that if we have damage absorption we take the correct damage - destroyableComponent->SetDamageToAbsorb(10); - destroyableComponent->Damage(9, LWOOBJID_EMPTY); - ASSERT_EQ(destroyableComponent->GetHealth(), 85); - ASSERT_EQ(destroyableComponent->GetDamageToAbsorb(), 1); - destroyableComponent->Damage(6, LWOOBJID_EMPTY); - ASSERT_EQ(destroyableComponent->GetHealth(), 80); - // Check that we take the correct reduced damage if we take reduced damage - destroyableComponent->SetDamageReduction(2); - destroyableComponent->Damage(7, LWOOBJID_EMPTY); - ASSERT_EQ(destroyableComponent->GetHealth(), 75); - destroyableComponent->Damage(2, LWOOBJID_EMPTY); - ASSERT_EQ(destroyableComponent->GetHealth(), 74); - ASSERT_EQ(destroyableComponent->GetDamageReduction(), 2); - destroyableComponent->SetDamageReduction(0); - // Check that blocking works - destroyableComponent->SetAttacksToBlock(1); - destroyableComponent->Damage(UINT32_MAX, LWOOBJID_EMPTY); - ASSERT_EQ(destroyableComponent->GetHealth(), 74); - destroyableComponent->Damage(4, LWOOBJID_EMPTY); - ASSERT_EQ(destroyableComponent->GetHealth(), 70); - // Check that immunity works - destroyableComponent->SetIsImmune(true); - destroyableComponent->Damage(UINT32_MAX, LWOOBJID_EMPTY); - ASSERT_EQ(destroyableComponent->GetHealth(), 70); - ASSERT_TRUE(destroyableComponent->IsImmune()); - destroyableComponent->SetIsImmune(false); - destroyableComponent->SetIsGMImmune(true); - destroyableComponent->Damage(UINT32_MAX, LWOOBJID_EMPTY); - ASSERT_EQ(destroyableComponent->GetHealth(), 70); - ASSERT_TRUE(destroyableComponent->IsImmune()); - destroyableComponent->SetIsGMImmune(false); - // Check knockback immunity - destroyableComponent->SetIsShielded(true); - ASSERT_TRUE(destroyableComponent->IsKnockbackImmune()); - // Finally deal enough damage to kill the Entity - destroyableComponent->Damage(71, LWOOBJID_EMPTY); - ASSERT_EQ(destroyableComponent->GetHealth(), 0); - // Now lets heal some stats - destroyableComponent->Heal(15); - ASSERT_EQ(destroyableComponent->GetHealth(), 15); - destroyableComponent->Heal(15000); - ASSERT_EQ(destroyableComponent->GetHealth(), 100); - destroyableComponent->Repair(10); - ASSERT_EQ(destroyableComponent->GetArmor(), 10); - destroyableComponent->Repair(15000); - ASSERT_EQ(destroyableComponent->GetArmor(), 10); - destroyableComponent->SetMaxImagination(100.0f); - destroyableComponent->SetImagination(0); - destroyableComponent->Imagine(99); - ASSERT_EQ(destroyableComponent->GetImagination(), 99); - destroyableComponent->Imagine(4); - ASSERT_EQ(destroyableComponent->GetImagination(), 100); -} - -TEST_F(DestroyableTest, DestroyableComponentFactionTest) { - ASSERT_TRUE(destroyableComponent->HasFaction(-1)); - ASSERT_TRUE(destroyableComponent->HasFaction(6)); -} - -TEST_F(DestroyableTest, DestroyableComponentValiditiyTest) { - auto* enemyEntity = new Entity(19, info); - enemyEntity->AddComponent()->AddFactionNoLookup(16); - destroyableComponent->AddEnemyFaction(16); - EXPECT_TRUE(destroyableComponent->IsEnemy(enemyEntity)); - EXPECT_FALSE(destroyableComponent->IsFriend(enemyEntity)); - delete enemyEntity; -} - -TEST_F(DestroyableTest, DestroyableComponentImmunityTest) { - // assert to show that they are empty - ASSERT_FALSE(destroyableComponent->GetImmuneToBasicAttack()); - ASSERT_FALSE(destroyableComponent->GetImmuneToDamageOverTime()); - ASSERT_FALSE(destroyableComponent->GetImmuneToKnockback()); - ASSERT_FALSE(destroyableComponent->GetImmuneToInterrupt()); - ASSERT_FALSE(destroyableComponent->GetImmuneToSpeed()); - ASSERT_FALSE(destroyableComponent->GetImmuneToImaginationGain()); - ASSERT_FALSE(destroyableComponent->GetImmuneToImaginationLoss()); - ASSERT_FALSE(destroyableComponent->GetImmuneToQuickbuildInterrupt()); - ASSERT_FALSE(destroyableComponent->GetImmuneToPullToPoint()); - // set them all to true (count 1) and check - destroyableComponent->SetStatusImmunity(eStateChangeType::PUSH, true, true, true, true, true, true, true, true, true); - ASSERT_TRUE(destroyableComponent->GetImmuneToBasicAttack()); - ASSERT_TRUE(destroyableComponent->GetImmuneToDamageOverTime()); - ASSERT_TRUE(destroyableComponent->GetImmuneToKnockback()); - ASSERT_TRUE(destroyableComponent->GetImmuneToInterrupt()); - ASSERT_TRUE(destroyableComponent->GetImmuneToSpeed()); - ASSERT_TRUE(destroyableComponent->GetImmuneToImaginationGain()); - ASSERT_TRUE(destroyableComponent->GetImmuneToImaginationLoss()); - ASSERT_TRUE(destroyableComponent->GetImmuneToQuickbuildInterrupt()); - ASSERT_TRUE(destroyableComponent->GetImmuneToPullToPoint()); - - // remove them to check that they get removed properly - destroyableComponent->SetStatusImmunity(eStateChangeType::POP, true, true, true, true, true, true, true, true, true); - ASSERT_FALSE(destroyableComponent->GetImmuneToBasicAttack()); - ASSERT_FALSE(destroyableComponent->GetImmuneToDamageOverTime()); - ASSERT_FALSE(destroyableComponent->GetImmuneToKnockback()); - ASSERT_FALSE(destroyableComponent->GetImmuneToInterrupt()); - ASSERT_FALSE(destroyableComponent->GetImmuneToSpeed()); - ASSERT_FALSE(destroyableComponent->GetImmuneToImaginationGain()); - ASSERT_FALSE(destroyableComponent->GetImmuneToImaginationLoss()); - ASSERT_FALSE(destroyableComponent->GetImmuneToQuickbuildInterrupt()); - ASSERT_FALSE(destroyableComponent->GetImmuneToPullToPoint()); - - // should not crash to remove them again - destroyableComponent->SetStatusImmunity(eStateChangeType::POP, true, true, true, true, true, true, true, true, true); - ASSERT_FALSE(destroyableComponent->GetImmuneToBasicAttack()); - ASSERT_FALSE(destroyableComponent->GetImmuneToDamageOverTime()); - ASSERT_FALSE(destroyableComponent->GetImmuneToKnockback()); - ASSERT_FALSE(destroyableComponent->GetImmuneToInterrupt()); - ASSERT_FALSE(destroyableComponent->GetImmuneToSpeed()); - ASSERT_FALSE(destroyableComponent->GetImmuneToImaginationGain()); - ASSERT_FALSE(destroyableComponent->GetImmuneToImaginationLoss()); - ASSERT_FALSE(destroyableComponent->GetImmuneToQuickbuildInterrupt()); - ASSERT_FALSE(destroyableComponent->GetImmuneToPullToPoint()); - - // just do one - destroyableComponent->SetStatusImmunity(eStateChangeType::PUSH, true); - ASSERT_TRUE(destroyableComponent->GetImmuneToBasicAttack()); - ASSERT_FALSE(destroyableComponent->GetImmuneToDamageOverTime()); - ASSERT_FALSE(destroyableComponent->GetImmuneToKnockback()); - ASSERT_FALSE(destroyableComponent->GetImmuneToInterrupt()); - ASSERT_FALSE(destroyableComponent->GetImmuneToSpeed()); - ASSERT_FALSE(destroyableComponent->GetImmuneToImaginationGain()); - ASSERT_FALSE(destroyableComponent->GetImmuneToImaginationLoss()); - ASSERT_FALSE(destroyableComponent->GetImmuneToQuickbuildInterrupt()); - ASSERT_FALSE(destroyableComponent->GetImmuneToPullToPoint()); - // now stack it to 2 on basic attack - destroyableComponent->SetStatusImmunity(eStateChangeType::PUSH, true); - ASSERT_TRUE(destroyableComponent->GetImmuneToBasicAttack()); - ASSERT_FALSE(destroyableComponent->GetImmuneToDamageOverTime()); - ASSERT_FALSE(destroyableComponent->GetImmuneToKnockback()); - ASSERT_FALSE(destroyableComponent->GetImmuneToInterrupt()); - ASSERT_FALSE(destroyableComponent->GetImmuneToSpeed()); - ASSERT_FALSE(destroyableComponent->GetImmuneToImaginationGain()); - ASSERT_FALSE(destroyableComponent->GetImmuneToImaginationLoss()); - ASSERT_FALSE(destroyableComponent->GetImmuneToQuickbuildInterrupt()); - ASSERT_FALSE(destroyableComponent->GetImmuneToPullToPoint()); - // remove one and still shoudl be true - destroyableComponent->SetStatusImmunity(eStateChangeType::POP, true); - ASSERT_TRUE(destroyableComponent->GetImmuneToBasicAttack()); - ASSERT_FALSE(destroyableComponent->GetImmuneToDamageOverTime()); - ASSERT_FALSE(destroyableComponent->GetImmuneToKnockback()); - ASSERT_FALSE(destroyableComponent->GetImmuneToInterrupt()); - ASSERT_FALSE(destroyableComponent->GetImmuneToSpeed()); - ASSERT_FALSE(destroyableComponent->GetImmuneToImaginationGain()); - ASSERT_FALSE(destroyableComponent->GetImmuneToImaginationLoss()); - ASSERT_FALSE(destroyableComponent->GetImmuneToQuickbuildInterrupt()); - ASSERT_FALSE(destroyableComponent->GetImmuneToPullToPoint()); - // go back to 0 - destroyableComponent->SetStatusImmunity(eStateChangeType::POP, true); - ASSERT_FALSE(destroyableComponent->GetImmuneToBasicAttack()); - ASSERT_FALSE(destroyableComponent->GetImmuneToDamageOverTime()); - ASSERT_FALSE(destroyableComponent->GetImmuneToKnockback()); - ASSERT_FALSE(destroyableComponent->GetImmuneToInterrupt()); - ASSERT_FALSE(destroyableComponent->GetImmuneToSpeed()); - ASSERT_FALSE(destroyableComponent->GetImmuneToImaginationGain()); - ASSERT_FALSE(destroyableComponent->GetImmuneToImaginationLoss()); - ASSERT_FALSE(destroyableComponent->GetImmuneToQuickbuildInterrupt()); - ASSERT_FALSE(destroyableComponent->GetImmuneToPullToPoint()); - // check individual ones now - destroyableComponent->SetStatusImmunity(eStateChangeType::PUSH, true, false, false, false, false, false, false, false, false); - ASSERT_TRUE(destroyableComponent->GetImmuneToBasicAttack()); - ASSERT_FALSE(destroyableComponent->GetImmuneToDamageOverTime()); - ASSERT_FALSE(destroyableComponent->GetImmuneToKnockback()); - ASSERT_FALSE(destroyableComponent->GetImmuneToInterrupt()); - ASSERT_FALSE(destroyableComponent->GetImmuneToSpeed()); - ASSERT_FALSE(destroyableComponent->GetImmuneToImaginationGain()); - ASSERT_FALSE(destroyableComponent->GetImmuneToImaginationLoss()); - ASSERT_FALSE(destroyableComponent->GetImmuneToQuickbuildInterrupt()); - ASSERT_FALSE(destroyableComponent->GetImmuneToPullToPoint()); - destroyableComponent->SetStatusImmunity(eStateChangeType::POP, true, false, false, false, false, false, false, false, false); - - destroyableComponent->SetStatusImmunity(eStateChangeType::PUSH, false, true, false, false, false, false, false, false, false); - ASSERT_FALSE(destroyableComponent->GetImmuneToBasicAttack()); - ASSERT_TRUE(destroyableComponent->GetImmuneToDamageOverTime()); - ASSERT_FALSE(destroyableComponent->GetImmuneToKnockback()); - ASSERT_FALSE(destroyableComponent->GetImmuneToInterrupt()); - ASSERT_FALSE(destroyableComponent->GetImmuneToSpeed()); - ASSERT_FALSE(destroyableComponent->GetImmuneToImaginationGain()); - ASSERT_FALSE(destroyableComponent->GetImmuneToImaginationLoss()); - ASSERT_FALSE(destroyableComponent->GetImmuneToQuickbuildInterrupt()); - ASSERT_FALSE(destroyableComponent->GetImmuneToPullToPoint()); - destroyableComponent->SetStatusImmunity(eStateChangeType::POP, false, true, false, false, false, false, false, false, false); - - destroyableComponent->SetStatusImmunity(eStateChangeType::PUSH, false, false, true, false, false, false, false, false, false); - ASSERT_FALSE(destroyableComponent->GetImmuneToBasicAttack()); - ASSERT_FALSE(destroyableComponent->GetImmuneToDamageOverTime()); - ASSERT_TRUE(destroyableComponent->GetImmuneToKnockback()); - ASSERT_FALSE(destroyableComponent->GetImmuneToInterrupt()); - ASSERT_FALSE(destroyableComponent->GetImmuneToSpeed()); - ASSERT_FALSE(destroyableComponent->GetImmuneToImaginationGain()); - ASSERT_FALSE(destroyableComponent->GetImmuneToImaginationLoss()); - ASSERT_FALSE(destroyableComponent->GetImmuneToQuickbuildInterrupt()); - ASSERT_FALSE(destroyableComponent->GetImmuneToPullToPoint()); - destroyableComponent->SetStatusImmunity(eStateChangeType::POP, false, false, true, false, false, false, false, false, false); - - destroyableComponent->SetStatusImmunity(eStateChangeType::PUSH, false, false, false, true, false, false, false, false, false); - ASSERT_FALSE(destroyableComponent->GetImmuneToBasicAttack()); - ASSERT_FALSE(destroyableComponent->GetImmuneToDamageOverTime()); - ASSERT_FALSE(destroyableComponent->GetImmuneToKnockback()); - ASSERT_TRUE(destroyableComponent->GetImmuneToInterrupt()); - ASSERT_FALSE(destroyableComponent->GetImmuneToSpeed()); - ASSERT_FALSE(destroyableComponent->GetImmuneToImaginationGain()); - ASSERT_FALSE(destroyableComponent->GetImmuneToImaginationLoss()); - ASSERT_FALSE(destroyableComponent->GetImmuneToQuickbuildInterrupt()); - ASSERT_FALSE(destroyableComponent->GetImmuneToPullToPoint()); - destroyableComponent->SetStatusImmunity(eStateChangeType::POP, false, false, false, true, false, false, false, false, false); - - destroyableComponent->SetStatusImmunity(eStateChangeType::PUSH, false, false, false, false, true, false, false, false, false); - ASSERT_FALSE(destroyableComponent->GetImmuneToBasicAttack()); - ASSERT_FALSE(destroyableComponent->GetImmuneToDamageOverTime()); - ASSERT_FALSE(destroyableComponent->GetImmuneToKnockback()); - ASSERT_FALSE(destroyableComponent->GetImmuneToInterrupt()); - ASSERT_TRUE(destroyableComponent->GetImmuneToSpeed()); - ASSERT_FALSE(destroyableComponent->GetImmuneToImaginationGain()); - ASSERT_FALSE(destroyableComponent->GetImmuneToImaginationLoss()); - ASSERT_FALSE(destroyableComponent->GetImmuneToQuickbuildInterrupt()); - ASSERT_FALSE(destroyableComponent->GetImmuneToPullToPoint()); - destroyableComponent->SetStatusImmunity(eStateChangeType::POP, false, false, false, false, true, false, false, false, false); - - destroyableComponent->SetStatusImmunity(eStateChangeType::PUSH, false, false, false, false, false, true, false, false, false); - ASSERT_FALSE(destroyableComponent->GetImmuneToBasicAttack()); - ASSERT_FALSE(destroyableComponent->GetImmuneToDamageOverTime()); - ASSERT_FALSE(destroyableComponent->GetImmuneToKnockback()); - ASSERT_FALSE(destroyableComponent->GetImmuneToInterrupt()); - ASSERT_FALSE(destroyableComponent->GetImmuneToSpeed()); - ASSERT_TRUE(destroyableComponent->GetImmuneToImaginationGain()); - ASSERT_FALSE(destroyableComponent->GetImmuneToImaginationLoss()); - ASSERT_FALSE(destroyableComponent->GetImmuneToQuickbuildInterrupt()); - ASSERT_FALSE(destroyableComponent->GetImmuneToPullToPoint()); - destroyableComponent->SetStatusImmunity(eStateChangeType::POP, false, false, false, false, false, true, false, false, false); - - destroyableComponent->SetStatusImmunity(eStateChangeType::PUSH, false, false, false, false, false, false, true, false, false); - ASSERT_FALSE(destroyableComponent->GetImmuneToBasicAttack()); - ASSERT_FALSE(destroyableComponent->GetImmuneToDamageOverTime()); - ASSERT_FALSE(destroyableComponent->GetImmuneToKnockback()); - ASSERT_FALSE(destroyableComponent->GetImmuneToInterrupt()); - ASSERT_FALSE(destroyableComponent->GetImmuneToSpeed()); - ASSERT_FALSE(destroyableComponent->GetImmuneToImaginationGain()); - ASSERT_TRUE(destroyableComponent->GetImmuneToImaginationLoss()); - ASSERT_FALSE(destroyableComponent->GetImmuneToQuickbuildInterrupt()); - ASSERT_FALSE(destroyableComponent->GetImmuneToPullToPoint()); - destroyableComponent->SetStatusImmunity(eStateChangeType::POP, false, false, false, false, false, false, true, false, false); - - - destroyableComponent->SetStatusImmunity(eStateChangeType::PUSH, false, false, false, false, false, false, false, true, false); - ASSERT_FALSE(destroyableComponent->GetImmuneToBasicAttack()); - ASSERT_FALSE(destroyableComponent->GetImmuneToDamageOverTime()); - ASSERT_FALSE(destroyableComponent->GetImmuneToKnockback()); - ASSERT_FALSE(destroyableComponent->GetImmuneToInterrupt()); - ASSERT_FALSE(destroyableComponent->GetImmuneToSpeed()); - ASSERT_FALSE(destroyableComponent->GetImmuneToImaginationGain()); - ASSERT_FALSE(destroyableComponent->GetImmuneToImaginationLoss()); - ASSERT_TRUE(destroyableComponent->GetImmuneToQuickbuildInterrupt()); - ASSERT_FALSE(destroyableComponent->GetImmuneToPullToPoint()); - destroyableComponent->SetStatusImmunity(eStateChangeType::POP, false, false, false, false, false, false, false, true, false); - - - destroyableComponent->SetStatusImmunity(eStateChangeType::PUSH, false, false, false, false, false, false, false, false, true); - ASSERT_FALSE(destroyableComponent->GetImmuneToBasicAttack()); - ASSERT_FALSE(destroyableComponent->GetImmuneToDamageOverTime()); - ASSERT_FALSE(destroyableComponent->GetImmuneToKnockback()); - ASSERT_FALSE(destroyableComponent->GetImmuneToInterrupt()); - ASSERT_FALSE(destroyableComponent->GetImmuneToSpeed()); - ASSERT_FALSE(destroyableComponent->GetImmuneToImaginationGain()); - ASSERT_FALSE(destroyableComponent->GetImmuneToImaginationLoss()); - ASSERT_FALSE(destroyableComponent->GetImmuneToQuickbuildInterrupt()); - ASSERT_TRUE(destroyableComponent->GetImmuneToPullToPoint()); - destroyableComponent->SetStatusImmunity(eStateChangeType::POP, false, false, false, false, false, false, false, false, true); - -}*/ - -/** - * Test the Damage cooldown timer of DestroyableComponent - */ -/*TEST_F(DestroyableTest, DestroyableComponentDamageCooldownTest) { - // Test the damage immune timer state (anything above 0.0f) - destroyableComponent->SetDamageCooldownTimer(1.0f); - EXPECT_FLOAT_EQ(destroyableComponent->GetDamageCooldownTimer(), 1.0f); - ASSERT_TRUE(destroyableComponent->IsCooldownImmune()); - - // Test that the Update() function correctly decrements the damage cooldown timer - destroyableComponent->Update(0.5f); - EXPECT_FLOAT_EQ(destroyableComponent->GetDamageCooldownTimer(), 0.5f); - ASSERT_TRUE(destroyableComponent->IsCooldownImmune()); - - // Test the non damage immune timer state (anything below or equal to 0.0f) - destroyableComponent->SetDamageCooldownTimer(0.0f); - EXPECT_FLOAT_EQ(destroyableComponent->GetDamageCooldownTimer(), 0.0f); - ASSERT_FALSE(destroyableComponent->IsCooldownImmune()); -}*/ - + * Test bitset pet flags +*/ TEST_F(PetTest, PetComponentFlagTest) { // Test setting and reading single flags - petComponent->SetFlag(PetFlag::NOT_WAITING); - ASSERT_TRUE(petComponent->HasFlag(PetFlag::NOT_WAITING)); - ASSERT_FALSE(petComponent->HasFlag(PetFlag::NOT_WAITING)); - - // Test + petComponent->SetFlag(NOT_WAITING); + ASSERT_TRUE(petComponent->HasFlag(NOT_WAITING)); + ASSERT_FALSE(petComponent->HasFlag(SPAWNING)); + + // Test setting and reading multiple flags + petComponent->SetFlag(TAMEABLE, BEING_TAMED); + ASSERT_TRUE(petComponent->HasFlag(TAMEABLE, BEING_TAMED)); + ASSERT_TRUE(petComponent->HasFlag(NOT_WAITING)); + ASSERT_FALSE(petComponent->HasFlag(NOT_WAITING, SPAWNING)); + ASSERT_TRUE(petComponent->HasFlag(NOT_WAITING, TAMEABLE, BEING_TAMED)); + ASSERT_FALSE(petComponent->HasFlag(SPAWNING)); + ASSERT_FALSE(petComponent->HasFlag(SPAWNING, NOT_WAITING, TAMEABLE, BEING_TAMED)); + + // Test unsetting and reading multiple flags + petComponent->UnsetFlag(NOT_WAITING, SPAWNING); + ASSERT_FALSE(petComponent->HasFlag(NOT_WAITING, SPAWNING)); + ASSERT_FALSE(petComponent->HasFlag(NOT_WAITING)); + ASSERT_TRUE(petComponent->HasFlag(TAMEABLE, BEING_TAMED)); + ASSERT_FALSE(petComponent->HasFlag(NOT_WAITING, TAMEABLE, BEING_TAMED)); + ASSERT_FALSE(petComponent->HasFlag(SPAWNING)); + ASSERT_FALSE(petComponent->HasFlag(SPAWNING, NOT_WAITING, TAMEABLE, BEING_TAMED)); } From e01fbfcc648dae2d0f16ae1acce8bd8b4f776799 Mon Sep 17 00:00:00 2001 From: jadebenn Date: Fri, 15 Dec 2023 12:57:13 -0600 Subject: [PATCH 37/73] restored header guards --- dCommon/dEnums/eHelpType.h | 5 ++++- dCommon/dEnums/ePetAbilityType.h | 7 ++++--- dGame/dComponents/BaseCombatAIComponent.h | 5 ++++- dGame/dGameMessages/GameMessages.h | 5 ++++- 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/dCommon/dEnums/eHelpType.h b/dCommon/dEnums/eHelpType.h index 94f584e62..c2fa70cd0 100644 --- a/dCommon/dEnums/eHelpType.h +++ b/dCommon/dEnums/eHelpType.h @@ -1,4 +1,5 @@ -#pragma once +#ifndef __EHELPTYPE__H__ +#define __EHELPTYPE__H__ #include @@ -35,3 +36,5 @@ enum class eHelpType : int32_t { PET_DESPAWN_TAMING_NEW_PET = 70, UI_INVENTORY_FULL_CANNOT_PICKUP_ITEM = 86 }; + +#endif //!__EHELPTYPE__H__ diff --git a/dCommon/dEnums/ePetAbilityType.h b/dCommon/dEnums/ePetAbilityType.h index 70cf75c6c..3b2e55204 100644 --- a/dCommon/dEnums/ePetAbilityType.h +++ b/dCommon/dEnums/ePetAbilityType.h @@ -1,6 +1,5 @@ -#pragma once - -#include +#ifndef __EPETABILITYTYPE__H__ +#define __EPETABILITYTYPE__H__ enum class ePetAbilityType : int32_t { Invalid, @@ -8,3 +7,5 @@ enum class ePetAbilityType : int32_t { JumpOnObject, DigAtPosition }; + +#endif //!__EPETABILITYTYPE__H__ diff --git a/dGame/dComponents/BaseCombatAIComponent.h b/dGame/dComponents/BaseCombatAIComponent.h index f85fa70f4..a08b008e4 100644 --- a/dGame/dComponents/BaseCombatAIComponent.h +++ b/dGame/dComponents/BaseCombatAIComponent.h @@ -1,4 +1,5 @@ -#pragma once +#ifndef BASECOMBATAICOMPONENT_H +#define BASECOMBATAICOMPONENT_H #include "RakNetTypes.h" #include "dCommonVars.h" @@ -387,3 +388,5 @@ class BaseCombatAIComponent : public Component { */ bool IsMech(); }; + +#endif // BASECOMBATAICOMPONENT_H diff --git a/dGame/dGameMessages/GameMessages.h b/dGame/dGameMessages/GameMessages.h index f17919047..6099f2049 100644 --- a/dGame/dGameMessages/GameMessages.h +++ b/dGame/dGameMessages/GameMessages.h @@ -1,4 +1,5 @@ -#pragma once +#ifndef GAMEMESSAGES_H +#define GAMEMESSAGES_H #include "dCommonVars.h" #include @@ -661,3 +662,5 @@ namespace GameMessages { void HandleConfirmDonationOnPlayer(RakNet::BitStream* inStream, Entity* entity); void HandleCancelDonationOnPlayer(RakNet::BitStream* inStream, Entity* entity); }; + +#endif // GAMEMESSAGES_H From 9add2c944ea36f3ad09c737e27f8539e1c840ea2 Mon Sep 17 00:00:00 2001 From: jadebenn Date: Fri, 15 Dec 2023 14:36:27 -0600 Subject: [PATCH 38/73] PetFlag system now functioning correctly --- dGame/dComponents/PetComponent.cpp | 14 ++--- dGame/dComponents/PetComponent.h | 63 +++++++++---------- .../dComponentsTests/PetComponentTests.cpp | 39 +++++++++--- 3 files changed, 70 insertions(+), 46 deletions(-) diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index 08360380a..05461ba88 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -86,7 +86,7 @@ PetComponent::PetComponent(Entity* parent, uint32_t componentId): Component(pare m_TimerAway = 0; m_TimerBounce = 0; m_DatabaseId = LWOOBJID_EMPTY; - m_Flags = 1 << PetFlag::TAMEABLE; // Tameable + m_Flags = PetFlag::TAMEABLE; // Tameable m_Ability = ePetAbilityType::Invalid; m_StartPosition = m_Parent->GetPosition(); //NiPoint3::ZERO; m_MovementAI = nullptr; @@ -720,7 +720,7 @@ void PetComponent::ClientExitTamingMinigame(bool voluntaryExit) { currentActivities.erase(m_Tamer); - SetStatus(1 << PetFlag::TAMEABLE); + SetStatus(PetFlag::TAMEABLE); m_Tamer = LWOOBJID_EMPTY; m_Timer = 0; @@ -771,7 +771,7 @@ void PetComponent::ClientFailTamingMinigame() { currentActivities.erase(m_Tamer); - SetStatus(1 << PetFlag::TAMEABLE); + SetStatus(PetFlag::TAMEABLE); m_Tamer = LWOOBJID_EMPTY; m_Timer = 0; @@ -836,7 +836,7 @@ void PetComponent::OnSpawn() { m_Parent->SetOwnerOverride(m_Owner); m_MovementAI->SetMaxSpeed(m_SprintSpeed); m_MovementAI->SetHaltDistance(m_FollowRadius); - SetStatus(1 << PetFlag::NONE); + SetStatus(PetFlag::NONE); SetPetAiState(PetAiState::follow); } else { @@ -947,7 +947,7 @@ void PetComponent::StopInteract() { SetInteractType(PetInteractType::none); SetAbility(petAbility); SetPetAiState(PetAiState::follow); - SetStatus(1 << PetFlag::NONE); + SetStatus(PetFlag::NONE); SetIsReadyToInteract(false); SetIsHandlingInteraction(false); // Needed? m_MovementAI->SetMaxSpeed(m_SprintSpeed); @@ -977,7 +977,7 @@ void PetComponent::SetupInteractTreasureDig() { auto petAbility = ePetAbilityType::DigAtPosition; SetAbility(petAbility); - SetStatus(1 << PetFlag::NOT_WAITING); // TODO: Double-check this is the right flag being set + SetStatus(PetFlag::NOT_WAITING); // TODO: Double-check this is the right flag being set Game::entityManager->SerializeEntity(m_Parent); // TODO: Double-check pet packet captures const auto sysAddr = owner->GetSystemAddress(); @@ -1054,7 +1054,7 @@ void PetComponent::Activate(Item* item, bool registerPet, bool fromTaming) { // auto* owner = GetOwner(); if (owner == nullptr) return; - SetStatus(1 << PetFlag::SPAWNING); + SetFlag(SPAWNING); //SetStatus(PetFlag::SPAWNING); auto databaseData = inventoryComponent->GetDatabasePet(m_DatabaseId); diff --git a/dGame/dComponents/PetComponent.h b/dGame/dComponents/PetComponent.h index bb36b9241..32e6034f7 100644 --- a/dGame/dComponents/PetComponent.h +++ b/dGame/dComponents/PetComponent.h @@ -34,23 +34,15 @@ enum PetInteractType : uint8_t { */ enum PetFlag : uint32_t { NONE, - BEING_TAMED = 4, - NOT_WAITING = 5, - SPAWNING = 7, - TAMEABLE = 8 + BEING_TAMED = 1 << 4, //0x10, + NOT_WAITING = 1 << 5, //0x20, + SPAWNING = 1 << 7, //0x80 + TAMEABLE = 1 << 26 //0x4000000 }; -/* -* DEPRECATED The status of the pet: Governs the icon above their head and the interactions available +/** + * The pet emote animation ids that can used in PetComponent::Command() */ -/*enum PetStatus : uint32_t { - NONE, - BEING_TAMED = 1 << 4, //PetFlag::BEING_TAMED, //0x10, - IS_NOT_WAITING = 1 << 5, //PetFlag::NOT_WAITING, //0x20, - PLAY_SPAWN_ANIM = 1 << 7, //PetFlag::PLAY_SPAWN_ANIM, //0x80, - TAMEABLE = 1 << 8 // PetFlag::TAMEABLE //0x4000000 -};*/ - enum PetEmote : int32_t { ActivateSwitch = 201, DigTreasure, @@ -93,36 +85,43 @@ class PetComponent : public Component { /** * Sets one or more pet flags + * @param flag PetFlag(s) to set + */ + template + void SetFlag(varArg... flag) { m_Flags |= (static_cast(flag) | ...); } + + /** + * Sets the pet to ONLY have the specified flag(s), clearing all others + * @param flag PetFlag(s) to set exclusively */ template - void SetFlag(varArg... flag) { - const auto initFlags = m_Flags; - m_Flags |= (static_cast(1 << flag) | ...); - LOG_DEBUG("SetFlag: %d + %d = %d", initFlags, 1 << (flag | ...), m_Flags); - } + void SetOnlyFlag(varArg... flag) { m_Flags = (static_cast(flag) | ...); } /** * Unsets one or more pet flags + * @param flag PetFlag(s) to unset */ template - void UnsetFlag(varArg... flag) { - const auto initFlags = m_Flags; - m_Flags &= ~(static_cast(1 << flag) | ...); - LOG_DEBUG("UnsetFlag: %d - %d = %d", initFlags, 1 << (flag | ...), m_Flags); - } // THIS IS PROBLEMATIC + void UnsetFlag(varArg... flag) { m_Flags &= ~(static_cast(flag) | ...); } /** - * Gets one or more pet flags + * Returns true if the pet has all the specified flag(s) + * @param flag PetFlag(s) to check */ template - const bool HasFlag(varArg... flag) { - const auto lside = (m_Flags & (static_cast(1 << flag) | ...)); - const auto rside = (static_cast(1 << flag) | ...); - LOG_DEBUG("HasFlag: %d == %d", lside, rside); - return lside == rside; - //return (m_Flags & (static_cast(flag) | ...)) == (static_cast(flag) | ...); - } + const bool HasFlag(varArg... flag) { return (m_Flags & (static_cast(flag) | ...)) == (static_cast(flag) | ...); } + /** + * Returns true if the pet has ONLY the specified flag(s) + * @param flag PetFlag(s) to check if the pet has exclusively + */ + template + const bool HasOnlyFlag(varArg... flag) { return m_Flags == (static_cast(flag) | ...); } + + /** + * Governs the pet update loop + * @param deltaTime Time elapsed since last update + */ void Update(float deltaTime) override; /** diff --git a/tests/dGameTests/dComponentsTests/PetComponentTests.cpp b/tests/dGameTests/dComponentsTests/PetComponentTests.cpp index 2943ae0a1..9e063f210 100644 --- a/tests/dGameTests/dComponentsTests/PetComponentTests.cpp +++ b/tests/dGameTests/dComponentsTests/PetComponentTests.cpp @@ -22,9 +22,7 @@ class PetTest : public GameDependenciesTest { petComponent = baseEntity->AddComponent(1); // Initialize some values to be not default - petComponent->SetStatus(0); - petComponent->SetPetAiState(PetAiState::spawn); - petComponent->SetAbility(ePetAbilityType::Invalid); + } void TearDown() override { @@ -34,11 +32,14 @@ class PetTest : public GameDependenciesTest { }; TEST_F(PetTest, PlacementNewAddComponentTest) { + // Test adding component ASSERT_NE(petComponent, nullptr); baseEntity->AddComponent(1); ASSERT_NE(baseEntity->GetComponent(), nullptr); + + // Test getting initial status ASSERT_EQ(petComponent->GetParent()->GetObjectID(), 15); - ASSERT_EQ(petComponent->GetStatus(), PetFlag::NONE); + ASSERT_TRUE(petComponent->HasFlag(NONE)); ASSERT_EQ(petComponent->GetPetAiState(), PetAiState::spawn); ASSERT_EQ(petComponent->GetAbility(), ePetAbilityType::Invalid); } @@ -47,12 +48,36 @@ TEST_F(PetTest, PlacementNewAddComponentTest) { * Test bitset pet flags */ TEST_F(PetTest, PetComponentFlagTest) { - // Test setting and reading single flags + // Test setting and reading single flags, exclusively + petComponent->SetOnlyFlag(NONE); + ASSERT_TRUE(petComponent->HasOnlyFlag(NONE)); + petComponent->SetOnlyFlag(TAMEABLE); + ASSERT_TRUE(petComponent->HasOnlyFlag(TAMEABLE)); + ASSERT_FALSE(petComponent->HasOnlyFlag(SPAWNING)); + + // Test setting and reading multiple flags, exclusively + petComponent->SetOnlyFlag(NOT_WAITING, SPAWNING); + ASSERT_FALSE(petComponent->HasFlag(TAMEABLE)); + ASSERT_TRUE(petComponent->HasFlag(NOT_WAITING)); + ASSERT_TRUE(petComponent->HasFlag(SPAWNING)); + ASSERT_TRUE(petComponent->HasFlag(NOT_WAITING, SPAWNING)); + ASSERT_FALSE(petComponent->HasOnlyFlag(NOT_WAITING)); + ASSERT_FALSE(petComponent->HasOnlyFlag(SPAWNING)); + ASSERT_TRUE(petComponent->HasOnlyFlag(NOT_WAITING, SPAWNING)); + + // Test flags are being properly reset for next batch of tests + petComponent->SetOnlyFlag(NONE); + ASSERT_TRUE(petComponent->HasFlag(NONE)); + ASSERT_TRUE(petComponent->HasOnlyFlag(NONE)); + ASSERT_FALSE(petComponent->HasFlag(NOT_WAITING)); + ASSERT_FALSE(petComponent->HasFlag(NOT_WAITING, SPAWNING, TAMEABLE)); + + // Test setting and reading single flags, non-exclusively petComponent->SetFlag(NOT_WAITING); ASSERT_TRUE(petComponent->HasFlag(NOT_WAITING)); ASSERT_FALSE(petComponent->HasFlag(SPAWNING)); - // Test setting and reading multiple flags + // Test setting and reading multiple flags, non-exclusively petComponent->SetFlag(TAMEABLE, BEING_TAMED); ASSERT_TRUE(petComponent->HasFlag(TAMEABLE, BEING_TAMED)); ASSERT_TRUE(petComponent->HasFlag(NOT_WAITING)); @@ -61,7 +86,7 @@ TEST_F(PetTest, PetComponentFlagTest) { ASSERT_FALSE(petComponent->HasFlag(SPAWNING)); ASSERT_FALSE(petComponent->HasFlag(SPAWNING, NOT_WAITING, TAMEABLE, BEING_TAMED)); - // Test unsetting and reading multiple flags + // Test unsetting and reading multiple flags, non-exclusively petComponent->UnsetFlag(NOT_WAITING, SPAWNING); ASSERT_FALSE(petComponent->HasFlag(NOT_WAITING, SPAWNING)); ASSERT_FALSE(petComponent->HasFlag(NOT_WAITING)); From 1c7ce6eac32c88b715ce9e7096ee4aba0d335943 Mon Sep 17 00:00:00 2001 From: jadebenn Date: Fri, 15 Dec 2023 18:38:52 -0600 Subject: [PATCH 39/73] updated CDClient pet data handling --- .../CDClientDatabase/CDClientManager.cpp | 1 + .../CDClientTables/CDPetComponentTable.cpp | 34 ++++++++++++ .../CDClientTables/CDPetComponentTable.h | 32 +++++++++++ .../CDClientTables/CMakeLists.txt | 1 + dGame/dComponents/PetComponent.cpp | 55 ++++++++++--------- dGame/dComponents/PetComponent.h | 48 ++++++---------- 6 files changed, 116 insertions(+), 55 deletions(-) create mode 100644 dDatabase/CDClientDatabase/CDClientTables/CDPetComponentTable.cpp create mode 100644 dDatabase/CDClientDatabase/CDClientTables/CDPetComponentTable.h diff --git a/dDatabase/CDClientDatabase/CDClientManager.cpp b/dDatabase/CDClientDatabase/CDClientManager.cpp index 7c2f39537..bf5634b23 100644 --- a/dDatabase/CDClientDatabase/CDClientManager.cpp +++ b/dDatabase/CDClientDatabase/CDClientManager.cpp @@ -77,6 +77,7 @@ CDClientManager::CDClientManager() { CDCLIENT_DONT_CACHE_TABLE(CDObjectsTable::Instance().LoadValuesFromDatabase()); CDPhysicsComponentTable::Instance().LoadValuesFromDatabase(); CDPackageComponentTable::Instance().LoadValuesFromDatabase(); + CDPetComponentTable::Instance().LoadValuesFromDatabase(); CDProximityMonitorComponentTable::Instance().LoadValuesFromDatabase(); CDPropertyEntranceComponentTable::Instance().LoadValuesFromDatabase(); CDPropertyTemplateTable::Instance().LoadValuesFromDatabase(); diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDPetComponentTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDPetComponentTable.cpp new file mode 100644 index 000000000..03bf479d4 --- /dev/null +++ b/dDatabase/CDClientDatabase/CDClientTables/CDPetComponentTable.cpp @@ -0,0 +1,34 @@ +#include "CDPetComponentTable.h" + +void CDPetComponentTable::LoadValuesFromDatabase() { + auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM PetComponent"); + while (!tableData.eof()) { + CDPetComponent entry; + entry.id = tableData.getIntField("id", -1); + UNUSED_COLUMN(entry.minTameUpdateTime = tableData.getFloatField("minTameUpdateTime", 60.0f);) + UNUSED_COLUMN(entry.maxTameUpdateTime = tableData.getFloatField("maxTameUpdateTime", 300.0f);) + UNUSED_COLUMN(entry.percentTameChance = tableData.getFloatField("percentTameChance", 101.0f);) + UNUSED_COLUMN(entry.tameability = tableData.getFloatField("tamability", 100.0f);) // Mispelled as "tamability" in CDClient + UNUSED_COLUMN(entry.elementType = tableData.getIntField("elementType", 1);) + entry.walkSpeed = tableData.getFloatField("walkSpeed", 2.5f); + entry.runSpeed = tableData.getFloatField("runSpeed", 5.0f); + entry.sprintSpeed = tableData.getFloatField("sprintSpeed", 10.0f); + UNUSED_COLUMN(entry.idleTimeMin = tableData.getFloatField("idleTimeMin", 60.0f);) + UNUSED_COLUMN(entry.idleTimeMax = tableData.getFloatField("idleTimeMax", 300.0f);) + UNUSED_COLUMN(entry.petForm = tableData.getIntField("petForm", 0);) + entry.imaginationDrainRate = tableData.getFloatField("imaginationDrainRate", 60.0f); + UNUSED_COLUMN(entry.AudioMetaEventSet = tableData.getStringField("AudioMetaEventSet", "");) + UNUSED_COLUMN(entry.buffIDs = tableData.getStringField("buffIDs", "");) + + m_entries.insert(std::make_pair(entry.id, entry)); + tableData.nextRow(); + } + + tableData.finalize(); + +} + +CDPetComponent* CDPetComponentTable::GetByID(unsigned int componentID) { + auto itr = m_entries.find(componentID); + return itr != m_entries.end() ? &itr->second : nullptr; +} diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDPetComponentTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDPetComponentTable.h new file mode 100644 index 000000000..5e0d6477b --- /dev/null +++ b/dDatabase/CDClientDatabase/CDClientTables/CDPetComponentTable.h @@ -0,0 +1,32 @@ +#pragma once +#include "CDTable.h" +#include + +struct CDPetComponent { + unsigned int id; + UNUSED_COLUMN(float minTameUpdateTime;) + UNUSED_COLUMN(float maxTameUpdateTime;) + UNUSED_COLUMN(float percentTameChance;) + UNUSED_COLUMN(float tameability;) // Mispelled as "tamability" in CDClient + UNUSED_COLUMN(unsigned int elementType;) + float walkSpeed; + float runSpeed; + float sprintSpeed; + UNUSED_COLUMN(float idleTimeMin;) + UNUSED_COLUMN(float idleTimeMax;) + UNUSED_COLUMN(unsigned int petForm;) + float imaginationDrainRate; + UNUSED_COLUMN(std::string AudioMetaEventSet;) + UNUSED_COLUMN(std::string buffIDs;) +}; + +class CDPetComponentTable : public CDTable { +public: + void LoadValuesFromDatabase(); + + static const std::string GetTableName() { return "PetComponent"; }; + CDPetComponent* GetByID(unsigned int componentID); + +private: + std::map m_entries; +}; diff --git a/dDatabase/CDClientDatabase/CDClientTables/CMakeLists.txt b/dDatabase/CDClientDatabase/CDClientTables/CMakeLists.txt index b2551efa3..af401db29 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CMakeLists.txt +++ b/dDatabase/CDClientDatabase/CDClientTables/CMakeLists.txt @@ -23,6 +23,7 @@ set(DDATABASE_CDCLIENTDATABASE_CDCLIENTTABLES_SOURCES "CDActivitiesTable.cpp" "CDMovementAIComponentTable.cpp" "CDObjectSkillsTable.cpp" "CDObjectsTable.cpp" + "CDPetComponentTable.cpp" "CDPackageComponentTable.cpp" "CDPhysicsComponentTable.cpp" "CDPropertyEntranceComponentTable.cpp" diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index 05461ba88..14b2198e3 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -2,6 +2,7 @@ #include "GameMessages.h" #include "BrickDatabase.h" #include "CDClientDatabase.h" +#include "CDPetComponentTable.h" #include "ChatPackets.h" #include "EntityManager.h" #include "Character.h" @@ -37,7 +38,6 @@ std::unordered_map PetComponent::buildCache{}; std::unordered_map PetComponent::currentActivities{}; std::unordered_map PetComponent::activePets{}; -float PetComponent::m_FollowRadius{}; /** * Maps all the pet lots to a flag indicating that the player has caught it. All basic pets have been guessed by ObjID @@ -94,7 +94,6 @@ PetComponent::PetComponent(Entity* parent, uint32_t componentId): Component(pare m_ReadyToInteract = false; SetPetAiState(PetAiState::spawn); - m_FollowRadius = Game::zoneManager->GetPetFollowRadius(); SetIsHandlingInteraction(false); std::string checkPreconditions = GeneralUtils::UTF16ToWTF8(parent->GetVar(u"CheckPrecondition")); @@ -103,22 +102,28 @@ PetComponent::PetComponent(Entity* parent, uint32_t componentId): Component(pare SetPreconditions(checkPreconditions); } + // Load database values + m_FollowRadius = Game::zoneManager->GetPetFollowRadius(); + if (!GetPetInfo(m_ComponentId, m_PetInfo)) LOG("Failed to load PetComponent information from CDClient!"); + + /*m_PetInfo.id; + m_PetInfo.runSpeed; + m_PetInfo.sprintSpeed; + m_PetInfo.walkSpeed;*/ + + //CDClientManager::GetTable() //LoadDataFromTemplate(); // TODO: Figure out how to load this with the tests (DarkflameServer/dDatabase/CDClientDatabase/CDClientTables/) } -void PetComponent::LoadDataFromTemplate() { - auto query = CDClientDatabase::CreatePreppedStmt( - "SELECT walkSpeed, runSpeed, sprintSpeed, imaginationDrainRate FROM PetComponent WHERE id = ?;"); - query.bind(1, static_cast(m_ComponentId)); +bool PetComponent::GetPetInfo(uint32_t petId, CDPetComponent& result) { + auto* petTable = CDClientManager::Instance().GetTable(); - auto result = query.execQuery(); + const auto pet = petTable->GetByID(petId); + if (!pet) return false; - if (!result.eof()) { - m_WalkSpeed = result.getFloatField(0, 2.5f); - m_RunSpeed = result.getFloatField(1, 5.0f); - m_SprintSpeed = result.getFloatField(2, 10.0f); - imaginationDrainRate = result.getFloatField(3, 60.0f); - } + result = pet[0]; + + return true; } void PetComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) { @@ -216,6 +221,7 @@ void PetComponent::OnUse(Entity* originator) { std::string buildFile; + // TODO: MOVE THIS OUT OF THE COMPONENT if (cached == buildCache.end()) { auto query = CDClientDatabase::CreatePreppedStmt( "SELECT ValidPiecesLXF, PuzzleModelLot, Timelimit, NumValidPieces, imagCostPerBuild FROM TamingBuildPuzzles WHERE NPCLot = ?;"); @@ -338,7 +344,7 @@ void PetComponent::OnUse(Entity* originator) { GameMessages::SendNotifyPetTamingPuzzleSelected(originator->GetObjectID(), bricks, originator->GetSystemAddress()); m_Tamer = originator->GetObjectID(); - SetStatus(5); + SetFlag(UNKNOWN1, UNKNOWN4); //SetStatus(5); currentActivities.insert_or_assign(m_Tamer, m_Parent->GetObjectID()); @@ -607,7 +613,7 @@ void PetComponent::NotifyTamingBuildSuccess(NiPoint3 position) { missionComponent->Progress(eMissionTaskType::PET_TAMING, m_Parent->GetLOT()); } - SetStatus(1); + SetFlag(UNKNOWN1); // SetStatus(1); auto* characterComponent = tamer->GetComponent(); if (characterComponent != nullptr) { @@ -815,11 +821,11 @@ void PetComponent::Wander() { return; } - m_MovementAI->SetMaxSpeed(m_SprintSpeed); //info.wanderSpeed); + m_MovementAI->SetMaxSpeed(m_PetInfo.sprintSpeed); //info.wanderSpeed); m_MovementAI->SetDestination(destination); - m_Timer += (m_MovementAI->GetParent()->GetPosition().x - destination.x) / m_SprintSpeed; //info.wanderSpeed; + m_Timer += (m_MovementAI->GetParent()->GetPosition().x - destination.x) / m_PetInfo.sprintSpeed; } void PetComponent::OnSpawn() { @@ -834,7 +840,7 @@ void PetComponent::OnSpawn() { if (m_Owner != LWOOBJID_EMPTY) { m_Parent->SetOwnerOverride(m_Owner); - m_MovementAI->SetMaxSpeed(m_SprintSpeed); + m_MovementAI->SetMaxSpeed(m_PetInfo.sprintSpeed); m_MovementAI->SetHaltDistance(m_FollowRadius); SetStatus(PetFlag::NONE); SetPetAiState(PetAiState::follow); @@ -931,7 +937,7 @@ void PetComponent::StartInteract(const NiPoint3 position, const PetInteractType SetInteractType(interactType); SetAbility(ePetAbilityType::GoToObject); SetPetAiState(PetAiState::goToObj); - m_MovementAI->SetMaxSpeed(m_RunSpeed); + m_MovementAI->SetMaxSpeed(m_PetInfo.runSpeed); m_MovementAI->SetHaltDistance(0.0f); m_MovementAI->SetDestination(position); LOG_DEBUG("Starting interaction!"); @@ -947,10 +953,10 @@ void PetComponent::StopInteract() { SetInteractType(PetInteractType::none); SetAbility(petAbility); SetPetAiState(PetAiState::follow); - SetStatus(PetFlag::NONE); + SetOnlyFlag(NONE); //SetStatus(PetFlag::NONE); SetIsReadyToInteract(false); SetIsHandlingInteraction(false); // Needed? - m_MovementAI->SetMaxSpeed(m_SprintSpeed); + m_MovementAI->SetMaxSpeed(m_PetInfo.sprintSpeed); m_MovementAI->SetHaltDistance(m_FollowRadius); LOG_DEBUG("Stopping interaction!"); @@ -977,7 +983,7 @@ void PetComponent::SetupInteractTreasureDig() { auto petAbility = ePetAbilityType::DigAtPosition; SetAbility(petAbility); - SetStatus(PetFlag::NOT_WAITING); // TODO: Double-check this is the right flag being set + SetFlag(NOT_WAITING); //SetStatus(PetFlag::NOT_WAITING); // TODO: Double-check this is the right flag being set Game::entityManager->SerializeEntity(m_Parent); // TODO: Double-check pet packet captures const auto sysAddr = owner->GetSystemAddress(); @@ -1007,8 +1013,7 @@ void PetComponent::StartInteractTreasureDig() { Game::entityManager->SerializeEntity(user); SetIsHandlingInteraction(true); - auto newStatus = GeneralUtils::ClearBit(GetStatus(), 6); - SetStatus(newStatus); // TODO: FIND THE CORRECT STATUS TO USE HERE + UnsetFlag(NOT_WAITING); // TODO: FIND THE CORRECT STATUS TO USE HERE Game::entityManager->SerializeEntity(m_Parent); Command(NiPoint3::ZERO, LWOOBJID_EMPTY, 1, PetEmote::DigTreasure, true); // Plays 'dig' animation @@ -1119,7 +1124,7 @@ void PetComponent::AddDrainImaginationTimer(Item* item, bool fromTaming) { if (!fromTaming) playerDestroyableComponent->Imagine(-1); // Set this to a variable so when this is called back from the player the timer doesn't fire off. - m_Parent->AddCallbackTimer(imaginationDrainRate, [playerDestroyableComponent, this, item]() { + m_Parent->AddCallbackTimer(m_PetInfo.imaginationDrainRate, [playerDestroyableComponent, this, item]() { if (!playerDestroyableComponent) { LOG("No petComponent and/or no playerDestroyableComponent"); return; diff --git a/dGame/dComponents/PetComponent.h b/dGame/dComponents/PetComponent.h index 32e6034f7..2c7d65049 100644 --- a/dGame/dComponents/PetComponent.h +++ b/dGame/dComponents/PetComponent.h @@ -7,6 +7,7 @@ #include "Preconditions.h" #include "eReplicaComponentType.h" #include "ePetAbilityType.h" +#include "CDPetComponentTable.h" /* * The current state of the pet AI @@ -34,6 +35,8 @@ enum PetInteractType : uint8_t { */ enum PetFlag : uint32_t { NONE, + UNKNOWN1 = 1 << 0, //0x01, + UNKNOWN4 = 1 << 2, //0x04, BEING_TAMED = 1 << 4, //0x10, NOT_WAITING = 1 << 5, //0x20, SPAWNING = 1 << 7, //0x80 @@ -61,10 +64,10 @@ class PetComponent : public Component { ~PetComponent() override; /** - * Loads pet data from CDClient + * Loads pet info from CDClient */ - void LoadDataFromTemplate(); //TODO: Move - + bool GetPetInfo(uint32_t petId, CDPetComponent& result); + /** * Serializes the pet * @param outBitStream The output bitstream @@ -88,35 +91,35 @@ class PetComponent : public Component { * @param flag PetFlag(s) to set */ template - void SetFlag(varArg... flag) { m_Flags |= (static_cast(flag) | ...); } + void SetFlag(varArg... flag) { m_Flags |= (static_cast(flag) | ...); }; /** * Sets the pet to ONLY have the specified flag(s), clearing all others * @param flag PetFlag(s) to set exclusively */ template - void SetOnlyFlag(varArg... flag) { m_Flags = (static_cast(flag) | ...); } + void SetOnlyFlag(varArg... flag) { m_Flags = (static_cast(flag) | ...); }; /** * Unsets one or more pet flags * @param flag PetFlag(s) to unset */ template - void UnsetFlag(varArg... flag) { m_Flags &= ~(static_cast(flag) | ...); } + void UnsetFlag(varArg... flag) { m_Flags &= ~(static_cast(flag) | ...); }; /** * Returns true if the pet has all the specified flag(s) * @param flag PetFlag(s) to check */ template - const bool HasFlag(varArg... flag) { return (m_Flags & (static_cast(flag) | ...)) == (static_cast(flag) | ...); } + const bool HasFlag(varArg... flag) { return (m_Flags & (static_cast(flag) | ...)) == (static_cast(flag) | ...); }; /** * Returns true if the pet has ONLY the specified flag(s) * @param flag PetFlag(s) to check if the pet has exclusively */ template - const bool HasOnlyFlag(varArg... flag) { return m_Flags == (static_cast(flag) | ...); } + const bool HasOnlyFlag(varArg... flag) { return m_Flags == (static_cast(flag) | ...); }; /** * Governs the pet update loop @@ -423,6 +426,11 @@ class PetComponent : public Component { int32_t numValidPieces; }; + /** + * Information that describes the different variables used to make a pet entity move around + */ + CDPetComponent m_PetInfo; + /** * Cache of all the pets that are currently spawned, indexed by tamer */ @@ -444,9 +452,9 @@ class PetComponent : public Component { static std::map petFlags; /** - * The halting radius of the pet while following a player + * The halting radius of the pet while following a player TODO: Move into struct? */ - static float m_FollowRadius; + float m_FollowRadius; /** * The ID of the component in the pet component table @@ -557,26 +565,6 @@ class PetComponent : public Component { * Preconditions that need to be met before an entity can tame this pet */ PreconditionExpression* m_Preconditions; - - /** - * The rate at which imagination is drained from the user for having the pet out. - */ - float imaginationDrainRate; - - /** - * The walk speed of the pet - */ - float m_WalkSpeed; - - /** - * The run speed of the pet - */ - float m_RunSpeed; - - /** - * The sprint speed of the pet - */ - float m_SprintSpeed; }; #endif // PETCOMPONENT_H From cecf0653c75162a688c3ffe85b0ed06c2acb26ef Mon Sep 17 00:00:00 2001 From: jadebenn Date: Fri, 15 Dec 2023 21:47:16 -0600 Subject: [PATCH 40/73] Changed how pet data is loaded --- dGame/dComponents/PetComponent.cpp | 35 ++++++++++++------------------ dGame/dComponents/PetComponent.h | 6 ++--- 2 files changed, 17 insertions(+), 24 deletions(-) diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index 14b2198e3..eea9f9fe9 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -104,20 +104,17 @@ PetComponent::PetComponent(Entity* parent, uint32_t componentId): Component(pare // Load database values m_FollowRadius = Game::zoneManager->GetPetFollowRadius(); - if (!GetPetInfo(m_ComponentId, m_PetInfo)) LOG("Failed to load PetComponent information from CDClient!"); - - /*m_PetInfo.id; - m_PetInfo.runSpeed; - m_PetInfo.sprintSpeed; - m_PetInfo.walkSpeed;*/ - - //CDClientManager::GetTable() - //LoadDataFromTemplate(); // TODO: Figure out how to load this with the tests (DarkflameServer/dDatabase/CDClientDatabase/CDClientTables/) + if (!LoadPetInfo(componentId, m_PetInfo)) LOG("Failed to load PetComponent (id: %d) information from CDClient!", componentId); } -bool PetComponent::GetPetInfo(uint32_t petId, CDPetComponent& result) { - auto* petTable = CDClientManager::Instance().GetTable(); - +bool PetComponent::LoadPetInfo(uint32_t petId, CDPetComponent& result) { + CDPetComponentTable* petTable; + try { + petTable = CDClientManager::Instance().GetTable(); + } catch(...) { + return false; + } + const auto pet = petTable->GetByID(petId); if (!pet) return false; @@ -381,7 +378,6 @@ void PetComponent::Update(float deltaTime) { // Handle pet AI states switch (m_State) { case PetAiState::spawn: - LOG_DEBUG("Pet spawn beginning!"); OnSpawn(); break; @@ -613,7 +609,7 @@ void PetComponent::NotifyTamingBuildSuccess(NiPoint3 position) { missionComponent->Progress(eMissionTaskType::PET_TAMING, m_Parent->GetLOT()); } - SetFlag(UNKNOWN1); // SetStatus(1); + SetOnlyFlag(UNKNOWN1); // SetStatus(1); auto* characterComponent = tamer->GetComponent(); if (characterComponent != nullptr) { @@ -726,7 +722,7 @@ void PetComponent::ClientExitTamingMinigame(bool voluntaryExit) { currentActivities.erase(m_Tamer); - SetStatus(PetFlag::TAMEABLE); + SetOnlyFlag(TAMEABLE); //SetStatus(PetFlag::TAMEABLE); m_Tamer = LWOOBJID_EMPTY; m_Timer = 0; @@ -777,7 +773,7 @@ void PetComponent::ClientFailTamingMinigame() { currentActivities.erase(m_Tamer); - SetStatus(PetFlag::TAMEABLE); + SetOnlyFlag(TAMEABLE); //SetStatus(PetFlag::TAMEABLE); m_Tamer = LWOOBJID_EMPTY; m_Timer = 0; @@ -830,19 +826,16 @@ void PetComponent::Wander() { void PetComponent::OnSpawn() { m_MovementAI = m_Parent->GetComponent(); - //if (!m_MovementAI) return; if (m_StartPosition == NiPoint3::ZERO) { m_StartPosition = m_Parent->GetPosition(); } - - LOG_DEBUG("Pet spawn complete, setting AI state."); if (m_Owner != LWOOBJID_EMPTY) { m_Parent->SetOwnerOverride(m_Owner); m_MovementAI->SetMaxSpeed(m_PetInfo.sprintSpeed); m_MovementAI->SetHaltDistance(m_FollowRadius); - SetStatus(PetFlag::NONE); + SetOnlyFlag(UNKNOWN1); //SetStatus(PetFlag::NONE); SetPetAiState(PetAiState::follow); } else { @@ -953,7 +946,7 @@ void PetComponent::StopInteract() { SetInteractType(PetInteractType::none); SetAbility(petAbility); SetPetAiState(PetAiState::follow); - SetOnlyFlag(NONE); //SetStatus(PetFlag::NONE); + SetOnlyFlag(UNKNOWN1); //SetStatus(PetFlag::NONE); SetIsReadyToInteract(false); SetIsHandlingInteraction(false); // Needed? m_MovementAI->SetMaxSpeed(m_PetInfo.sprintSpeed); diff --git a/dGame/dComponents/PetComponent.h b/dGame/dComponents/PetComponent.h index 2c7d65049..d0a4c13e3 100644 --- a/dGame/dComponents/PetComponent.h +++ b/dGame/dComponents/PetComponent.h @@ -35,7 +35,7 @@ enum PetInteractType : uint8_t { */ enum PetFlag : uint32_t { NONE, - UNKNOWN1 = 1 << 0, //0x01, + UNKNOWN1 = 1 << 0, //0x01 - Seems to be "idle," which the game doesn't differentiate from "follow" UNKNOWN4 = 1 << 2, //0x04, BEING_TAMED = 1 << 4, //0x10, NOT_WAITING = 1 << 5, //0x20, @@ -66,7 +66,7 @@ class PetComponent : public Component { /** * Loads pet info from CDClient */ - bool GetPetInfo(uint32_t petId, CDPetComponent& result); + bool LoadPetInfo(uint32_t petId, CDPetComponent& result); /** * Serializes the pet @@ -427,7 +427,7 @@ class PetComponent : public Component { }; /** - * Information that describes the different variables used to make a pet entity move around + * Pet information loaded from the CDClientDatabase */ CDPetComponent m_PetInfo; From 8c97271108d4c821a5ef2358a241f1f893e8ef5f Mon Sep 17 00:00:00 2001 From: jadebenn Date: Sat, 16 Dec 2023 19:55:43 -0600 Subject: [PATCH 41/73] Update treasure behavior --- dGame/dComponents/PetComponent.cpp | 63 ++++++++++++++++++++++-------- dGame/dComponents/PetComponent.h | 9 ++++- 2 files changed, 54 insertions(+), 18 deletions(-) diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index eea9f9fe9..16755c60c 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -35,6 +35,9 @@ #include "eGameMasterLevel.h" #include "eMissionState.h" +#define START_BITMASK_SWITCH(x) \ + for (uint32_t bit = 1; x >= bit; bit *= 2) if (x & bit) switch (bit) + std::unordered_map PetComponent::buildCache{}; std::unordered_map PetComponent::currentActivities{}; std::unordered_map PetComponent::activePets{}; @@ -86,7 +89,7 @@ PetComponent::PetComponent(Entity* parent, uint32_t componentId): Component(pare m_TimerAway = 0; m_TimerBounce = 0; m_DatabaseId = LWOOBJID_EMPTY; - m_Flags = PetFlag::TAMEABLE; // Tameable + m_Flags = PetFlag::SPAWNING; // Tameable m_Ability = ePetAbilityType::Invalid; m_StartPosition = m_Parent->GetPosition(); //NiPoint3::ZERO; m_MovementAI = nullptr; @@ -104,16 +107,11 @@ PetComponent::PetComponent(Entity* parent, uint32_t componentId): Component(pare // Load database values m_FollowRadius = Game::zoneManager->GetPetFollowRadius(); - if (!LoadPetInfo(componentId, m_PetInfo)) LOG("Failed to load PetComponent (id: %d) information from CDClient!", componentId); } bool PetComponent::LoadPetInfo(uint32_t petId, CDPetComponent& result) { CDPetComponentTable* petTable; - try { - petTable = CDClientManager::Instance().GetTable(); - } catch(...) { - return false; - } + petTable = CDClientManager::Instance().GetTable(); const auto pet = petTable->GetByID(petId); if (!pet) return false; @@ -375,12 +373,31 @@ void PetComponent::Update(float deltaTime) { } } - // Handle pet AI states - switch (m_State) { - case PetAiState::spawn: + /*START_BITMASK_SWITCH(m_Flags) { + case TAMEABLE: + break; + + case SPAWNING: + LOG_DEBUG("Has SPAWNING flag!"); OnSpawn(); break; + case NOT_WAITING: + LOG_DEBUG("Has NOT_WAITING flag!"); + break; + + case UNKNOWN1: + LOG_DEBUG("Has UNKNOWN1 flag!"); + break; + + default: + LOG_DEBUG("Triggered default case!"); + }*/ + + //if (HasFlag(SPAWNING)) OnSpawn(); + + // Handle pet AI states + switch (m_State) { case PetAiState::idle: Wander(); break; @@ -405,6 +422,7 @@ void PetComponent::Update(float deltaTime) { break; default: + LOG_DEBUG("Unknown state: %d!", m_Flags); break; } @@ -825,6 +843,9 @@ void PetComponent::Wander() { } void PetComponent::OnSpawn() { + if (!LoadPetInfo(m_ComponentId, m_PetInfo)) { + LOG("Failed to load PetComponent (id: %d) information from CDClient!", m_ComponentId); + } m_MovementAI = m_Parent->GetComponent(); if (m_StartPosition == NiPoint3::ZERO) { @@ -835,12 +856,17 @@ void PetComponent::OnSpawn() { m_Parent->SetOwnerOverride(m_Owner); m_MovementAI->SetMaxSpeed(m_PetInfo.sprintSpeed); m_MovementAI->SetHaltDistance(m_FollowRadius); - SetOnlyFlag(UNKNOWN1); //SetStatus(PetFlag::NONE); + //SetOnlyFlag(UNKNOWN1); //SetStatus(PetFlag::NONE); SetPetAiState(PetAiState::follow); } else { + SetFlag(TAMEABLE); SetPetAiState(PetAiState::idle); } + + SetFlag(UNKNOWN1); //IDLE? + UnsetFlag(SPAWNING); + Game::entityManager->SerializeEntity(m_Parent); } void PetComponent::OnFollow() { @@ -869,10 +895,9 @@ void PetComponent::OnFollow() { const bool nonDragonForBone = closestTreasure->GetLOT() == 12192 && m_Parent->GetLOT() != 13067; if (!nonDragonForBone && closestTreasure != nullptr && digUnlocked) { const NiPoint3 treasurePos = closestTreasure->GetPosition(); - const LWOOBJID treasureID = closestTreasure->GetObjectID(); const float distance = Vector3::DistanceSquared(ownerPos, treasurePos); if (distance < 16 * 16) { - StartInteract(treasurePos, PetInteractType::treasure, treasureID); + StartInteract(treasurePos, PetInteractType::treasure, m_Owner); return; } } @@ -940,6 +965,7 @@ void PetComponent::StartInteract(const NiPoint3 position, const PetInteractType void PetComponent::StopInteract() { Entity* owner = GetOwner(); if (!owner) return; + const auto petAbility = ePetAbilityType::Invalid; SetInteraction(LWOOBJID_EMPTY); @@ -977,6 +1003,7 @@ void PetComponent::SetupInteractTreasureDig() { SetAbility(petAbility); SetFlag(NOT_WAITING); //SetStatus(PetFlag::NOT_WAITING); // TODO: Double-check this is the right flag being set + LOG_DEBUG("m_Flags = %d", m_Flags); Game::entityManager->SerializeEntity(m_Parent); // TODO: Double-check pet packet captures const auto sysAddr = owner->GetSystemAddress(); @@ -1016,14 +1043,16 @@ void PetComponent::StartInteractTreasureDig() { void PetComponent::HandleInteractTreasureDig() { if (IsHandlingInteraction()) { auto* owner = GetOwner(); - auto* treasure = Game::entityManager->GetEntity(GetInteraction()); - if (!treasure || !owner) return; + if (!owner) return; + + auto* treasure = PetDigServer::GetClosestTresure(m_MovementAI->GetDestination()); // TODO: Find a better way to do this treasure->Smash(m_Parent->GetObjectID()); - LOG_DEBUG("Pet dig completed!"); GameMessages::SendHelp(m_Owner, eHelpType::PR_DIG_TUTORIAL_03, owner->GetSystemAddress()); + + LOG_DEBUG("Pet dig completed!"); StopInteract(); //TODO: This may not be totally consistent with live behavior, where the pet seems to stay near the dig and not immediately follow - //m_Timer = 1.5f; + return; } diff --git a/dGame/dComponents/PetComponent.h b/dGame/dComponents/PetComponent.h index d0a4c13e3..4e2209e5e 100644 --- a/dGame/dComponents/PetComponent.h +++ b/dGame/dComponents/PetComponent.h @@ -36,13 +36,20 @@ enum PetInteractType : uint8_t { enum PetFlag : uint32_t { NONE, UNKNOWN1 = 1 << 0, //0x01 - Seems to be "idle," which the game doesn't differentiate from "follow" - UNKNOWN4 = 1 << 2, //0x04, + UNKNOWN4 = 1 << 2, //0x04 - FOLLOWING(?) BEING_TAMED = 1 << 4, //0x10, NOT_WAITING = 1 << 5, //0x20, + STOP_MOVING = 1 << 6, //0x40 - Seems to be the "stop moving" flag - called when taming begins and stays active until a name is submitted SPAWNING = 1 << 7, //0x80 + UNKNOWN256 = 1 << 8, //0x100 + UNKNOWN1024 = 1 << 10, //0x400 TAMEABLE = 1 << 26 //0x4000000 }; +/* +132 = 128 + 4 +*/ + /** * The pet emote animation ids that can used in PetComponent::Command() */ From 2d3e03a2a8fba81322c549b1d6b2caff3ab41ac2 Mon Sep 17 00:00:00 2001 From: jadebenn Date: Sat, 16 Dec 2023 20:51:06 -0600 Subject: [PATCH 42/73] pet following and flag behavior improvements --- dGame/dComponents/PetComponent.cpp | 79 ++++++++++++++---------------- dGame/dComponents/PetComponent.h | 7 +-- 2 files changed, 42 insertions(+), 44 deletions(-) diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index 16755c60c..262f18302 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -339,7 +339,7 @@ void PetComponent::OnUse(Entity* originator) { GameMessages::SendNotifyPetTamingPuzzleSelected(originator->GetObjectID(), bricks, originator->GetSystemAddress()); m_Tamer = originator->GetObjectID(); - SetFlag(UNKNOWN1, UNKNOWN4); //SetStatus(5); + SetFlag(IDLE, UNKNOWN4); //SetStatus(5); currentActivities.insert_or_assign(m_Tamer, m_Parent->GetObjectID()); @@ -386,15 +386,15 @@ void PetComponent::Update(float deltaTime) { LOG_DEBUG("Has NOT_WAITING flag!"); break; - case UNKNOWN1: - LOG_DEBUG("Has UNKNOWN1 flag!"); + case IDLE: + LOG_DEBUG("Has IDLE flag!"); break; default: LOG_DEBUG("Triggered default case!"); }*/ - //if (HasFlag(SPAWNING)) OnSpawn(); + if (HasFlag(SPAWNING)) OnSpawn(); // Handle pet AI states switch (m_State) { @@ -403,7 +403,7 @@ void PetComponent::Update(float deltaTime) { break; case PetAiState::follow: - OnFollow(); + OnFollow(deltaTime); break; case PetAiState::goToObj: @@ -427,30 +427,6 @@ void PetComponent::Update(float deltaTime) { } /* - auto destination = owner->GetPosition(); - NiPoint3 position = m_MovementAI->GetParent()->GetPosition(); - - float distanceToOwner = Vector3::DistanceSquared(position, destination); - if (distanceToOwner > 50 * 50 || m_TimerAway > 5) { - m_MovementAI->Warp(destination); - - m_Timer = 1; - m_TimerAway = 0; - - return; - } - - if (distanceToOwner > 15 * 15 || std::abs(destination.y - position.y) >= 3) { - m_TimerAway += deltaTime; - } else { - m_TimerAway = 0; - } - - if (m_Timer > 0) { - m_Timer -= deltaTime; - return; - } - SwitchComponent* closestSwitch = SwitchComponent::GetClosestSwitch(position); float haltDistance = 5; @@ -627,7 +603,7 @@ void PetComponent::NotifyTamingBuildSuccess(NiPoint3 position) { missionComponent->Progress(eMissionTaskType::PET_TAMING, m_Parent->GetLOT()); } - SetOnlyFlag(UNKNOWN1); // SetStatus(1); + SetOnlyFlag(IDLE); // SetStatus(1); auto* characterComponent = tamer->GetComponent(); if (characterComponent != nullptr) { @@ -856,7 +832,7 @@ void PetComponent::OnSpawn() { m_Parent->SetOwnerOverride(m_Owner); m_MovementAI->SetMaxSpeed(m_PetInfo.sprintSpeed); m_MovementAI->SetHaltDistance(m_FollowRadius); - //SetOnlyFlag(UNKNOWN1); //SetStatus(PetFlag::NONE); + //SetOnlyFlag(IDLE); //SetStatus(PetFlag::NONE); SetPetAiState(PetAiState::follow); } else { @@ -864,14 +840,15 @@ void PetComponent::OnSpawn() { SetPetAiState(PetAiState::idle); } - SetFlag(UNKNOWN1); //IDLE? + SetFlag(IDLE); UnsetFlag(SPAWNING); Game::entityManager->SerializeEntity(m_Parent); } -void PetComponent::OnFollow() { +void PetComponent::OnFollow(const float& deltaTime) { Entity* owner = GetOwner(); if (!owner) return; + const NiPoint3 ownerPos = owner->GetPosition(); // Find interactions @@ -904,14 +881,28 @@ void PetComponent::OnFollow() { // Handle actual following logic const NiPoint3 currentPos = m_MovementAI->GetParent()->GetPosition(); + const float distanceToOwner = Vector3::DistanceSquared(currentPos, ownerPos); // If the player's position is within range, stop moving - if (Vector3::DistanceSquared(currentPos, ownerPos) <= m_FollowRadius * m_FollowRadius) { + if (distanceToOwner <= m_FollowRadius * m_FollowRadius) { m_MovementAI->Stop(); } else { // Chase the player's new position m_MovementAI->SetDestination(ownerPos); - LOG_DEBUG("New pet destination: %f %f %f", ownerPos.x, ownerPos.y, ownerPos.z); + //LOG_DEBUG("New pet destination: %f %f %f", ownerPos.x, ownerPos.y, ownerPos.z); + } + + // Teleporting logic + if (distanceToOwner > 50 * 50 || m_TimerAway > 5) { + m_MovementAI->Warp(ownerPos); + + m_Timer = 1; + m_TimerAway = 0; + + return; + } + else if (distanceToOwner > 15 * 15 || std::abs(ownerPos.y - currentPos.y) >= 3) { + m_TimerAway += deltaTime; } m_Timer += 0.5f; @@ -962,7 +953,7 @@ void PetComponent::StartInteract(const NiPoint3 position, const PetInteractType Game::entityManager->SerializeEntity(m_Parent); } -void PetComponent::StopInteract() { +void PetComponent::StopInteract(bool bDontSerialize) { Entity* owner = GetOwner(); if (!owner) return; @@ -972,14 +963,17 @@ void PetComponent::StopInteract() { SetInteractType(PetInteractType::none); SetAbility(petAbility); SetPetAiState(PetAiState::follow); - SetOnlyFlag(UNKNOWN1); //SetStatus(PetFlag::NONE); + SetOnlyFlag(IDLE); //SetStatus(PetFlag::NONE); SetIsReadyToInteract(false); SetIsHandlingInteraction(false); // Needed? m_MovementAI->SetMaxSpeed(m_PetInfo.sprintSpeed); m_MovementAI->SetHaltDistance(m_FollowRadius); LOG_DEBUG("Stopping interaction!"); - Game::entityManager->SerializeEntity(m_Parent); + if (!bDontSerialize) { + Game::entityManager->SerializeEntity(m_Parent); + } + GameMessages::SendShowPetActionButton(m_Owner, petAbility, false, owner->GetSystemAddress()); // Needed? } @@ -1002,7 +996,8 @@ void PetComponent::SetupInteractTreasureDig() { auto petAbility = ePetAbilityType::DigAtPosition; SetAbility(petAbility); - SetFlag(NOT_WAITING); //SetStatus(PetFlag::NOT_WAITING); // TODO: Double-check this is the right flag being set + UnsetFlag(IDLE); + SetFlag(UNKNOWN256, NOT_WAITING); //SetStatus(PetFlag::NOT_WAITING); // TODO: Double-check this is the right flag being set LOG_DEBUG("m_Flags = %d", m_Flags); Game::entityManager->SerializeEntity(m_Parent); // TODO: Double-check pet packet captures @@ -1033,7 +1028,9 @@ void PetComponent::StartInteractTreasureDig() { Game::entityManager->SerializeEntity(user); SetIsHandlingInteraction(true); - UnsetFlag(NOT_WAITING); // TODO: FIND THE CORRECT STATUS TO USE HERE + UnsetFlag(UNKNOWN256, NOT_WAITING); // TODO: FIND THE CORRECT STATUS TO USE HERE + SetFlag(IDLE); + LOG_DEBUG("StartInteractTreasureDig() m_Flags = %d", m_Flags); Game::entityManager->SerializeEntity(m_Parent); Command(NiPoint3::ZERO, LWOOBJID_EMPTY, 1, PetEmote::DigTreasure, true); // Plays 'dig' animation @@ -1051,7 +1048,7 @@ void PetComponent::HandleInteractTreasureDig() { GameMessages::SendHelp(m_Owner, eHelpType::PR_DIG_TUTORIAL_03, owner->GetSystemAddress()); LOG_DEBUG("Pet dig completed!"); - StopInteract(); //TODO: This may not be totally consistent with live behavior, where the pet seems to stay near the dig and not immediately follow + StopInteract(true); //TODO: This may not be totally consistent with live behavior, where the pet seems to stay near the dig and not immediately follow return; } diff --git a/dGame/dComponents/PetComponent.h b/dGame/dComponents/PetComponent.h index 4e2209e5e..f9b17c680 100644 --- a/dGame/dComponents/PetComponent.h +++ b/dGame/dComponents/PetComponent.h @@ -35,7 +35,7 @@ enum PetInteractType : uint8_t { */ enum PetFlag : uint32_t { NONE, - UNKNOWN1 = 1 << 0, //0x01 - Seems to be "idle," which the game doesn't differentiate from "follow" + IDLE = 1 << 0, //0x01 - Seems to be "idle," which the game doesn't differentiate from "follow" UNKNOWN4 = 1 << 2, //0x04 - FOLLOWING(?) BEING_TAMED = 1 << 4, //0x10, NOT_WAITING = 1 << 5, //0x20, @@ -197,7 +197,7 @@ class PetComponent : public Component { /** * Continues a step in the follow state, making sure that the entity is around its start position */ - void OnFollow(); + void OnFollow(const float& deltaTime); /** * Continues a step in the interact state, handling the pet's interaction with an entity @@ -211,8 +211,9 @@ class PetComponent : public Component { /** * Stop a pet interaction with an object + * @param bDontSerialize optional parameter, set to true to not serialize afterwards */ - void StopInteract(); + void StopInteract(bool bDontSerialize = false); /** * Set the type of interaction the pet is executing From c6df078411c3a48e387a1bb4222b80b9f6c64e99 Mon Sep 17 00:00:00 2001 From: jadebenn Date: Sat, 16 Dec 2023 21:25:35 -0600 Subject: [PATCH 43/73] comment cleanup --- dGame/dComponents/PetComponent.cpp | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index 262f18302..bd0fb583a 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -377,19 +377,6 @@ void PetComponent::Update(float deltaTime) { case TAMEABLE: break; - case SPAWNING: - LOG_DEBUG("Has SPAWNING flag!"); - OnSpawn(); - break; - - case NOT_WAITING: - LOG_DEBUG("Has NOT_WAITING flag!"); - break; - - case IDLE: - LOG_DEBUG("Has IDLE flag!"); - break; - default: LOG_DEBUG("Triggered default case!"); }*/ @@ -889,7 +876,6 @@ void PetComponent::OnFollow(const float& deltaTime) { } else { // Chase the player's new position m_MovementAI->SetDestination(ownerPos); - //LOG_DEBUG("New pet destination: %f %f %f", ownerPos.x, ownerPos.y, ownerPos.z); } // Teleporting logic From 6edd42d37c1bbc5d8d31dd924accac485d261c41 Mon Sep 17 00:00:00 2001 From: jadebenn Date: Wed, 27 Dec 2023 19:39:07 -0600 Subject: [PATCH 44/73] pet bouncer minimum functionality --- dGame/dComponents/PetComponent.cpp | 237 +++++++++++++++----------- dGame/dComponents/PetComponent.h | 21 ++- dGame/dComponents/SwitchComponent.cpp | 4 +- 3 files changed, 152 insertions(+), 110 deletions(-) diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index bd0fb583a..3e210e936 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -76,7 +76,7 @@ std::map PetComponent::petFlags = { { 13067, 838 }, // Skeleton dragon }; -PetComponent::PetComponent(Entity* parent, uint32_t componentId): Component(parent) { +PetComponent::PetComponent(Entity* parent, uint32_t componentId) : Component(parent) { m_ComponentId = componentId; m_Interaction = LWOOBJID_EMPTY; @@ -104,30 +104,18 @@ PetComponent::PetComponent(Entity* parent, uint32_t componentId): Component(pare if (!checkPreconditions.empty()) { SetPreconditions(checkPreconditions); } - + // Load database values m_FollowRadius = Game::zoneManager->GetPetFollowRadius(); } -bool PetComponent::LoadPetInfo(uint32_t petId, CDPetComponent& result) { - CDPetComponentTable* petTable; - petTable = CDClientManager::Instance().GetTable(); - - const auto pet = petTable->GetByID(petId); - if (!pet) return false; - - result = pet[0]; - - return true; -} - void PetComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) { const bool tamed = m_Owner != LWOOBJID_EMPTY; outBitStream->Write1(); // Always serialize as dirty for now outBitStream->Write(static_cast(m_Flags)); - outBitStream->Write(static_cast(tamed ? m_Ability : ePetAbilityType::Invalid)); // Something with the overhead icon? + outBitStream->Write(tamed ? m_Ability : ePetAbilityType::Invalid); // Something with the overhead icon? const bool interacting = m_Interaction != LWOOBJID_EMPTY; @@ -172,15 +160,15 @@ void PetComponent::OnUse(Entity* originator) { if (IsReadyToInteract()) { switch (GetAbility()) { - case ePetAbilityType::DigAtPosition: // Treasure dig TODO: FIX ICON + case ePetAbilityType::DigAtPosition: // Treasure dig TODO: FIX ICON StartInteractTreasureDig(); - break; - - case ePetAbilityType::JumpOnObject: // Bouncer - //StartInteractBouncer(); break; - default: + case ePetAbilityType::JumpOnObject: // Bouncer + StartInteractBouncer(); + break; + + default: break; } } @@ -350,12 +338,6 @@ void PetComponent::OnUse(Entity* originator) { } void PetComponent::Update(float deltaTime) { - // If pet does not have an owner, use the UpdateUnowned() loop - /*if (m_Owner == LWOOBJID_EMPTY) { - UpdateUnowned(deltaTime); - return; - }*/ - // Update timers m_TimerBounce -= deltaTime; @@ -392,14 +374,13 @@ void PetComponent::Update(float deltaTime) { case PetAiState::follow: OnFollow(deltaTime); break; - + case PetAiState::goToObj: if (m_MovementAI->AtFinalWaypoint()) { LOG_DEBUG("Reached object!"); m_MovementAI->Stop(); SetPetAiState(PetAiState::interact); - } - else { + } else { m_Timer += 0.5f; } break; @@ -412,24 +393,6 @@ void PetComponent::Update(float deltaTime) { LOG_DEBUG("Unknown state: %d!", m_Flags); break; } - - /* - SwitchComponent* closestSwitch = SwitchComponent::GetClosestSwitch(position); - - float haltDistance = 5; - - if (closestSwitch != nullptr && !closestSwitch->GetActive()) { - NiPoint3 switchPosition = closestSwitch->GetParentEntity()->GetPosition(); - float distance = Vector3::DistanceSquared(position, switchPosition); - if (distance < 3 * 3) { - m_Interaction = closestSwitch->GetParentEntity()->GetObjectID(); - closestSwitch->EntityEnter(m_Parent); - } else if (distance < 20 * 20) { - haltDistance = 1; - destination = switchPosition; - } - } - */ } void PetComponent::UpdateUnowned(float deltaTime) { //TODO: CURRENTLY UNUSED @@ -443,8 +406,7 @@ void PetComponent::UpdateUnowned(float deltaTime) { //TODO: CURRENTLY UNUSED ClientFailTamingMinigame(); } } - } - else { + } else { if (m_Timer > 0) { m_Timer -= deltaTime; @@ -551,7 +513,7 @@ void PetComponent::NotifyTamingBuildSuccess(NiPoint3 position) { GameMessages::SendRegisterPetDBID(m_Tamer, petSubKey, tamer->GetSystemAddress()); inventoryComponent->AddItem(m_Parent->GetLOT(), 1, eLootSourceType::ACTIVITY, eInventoryType::MODELS, {}, LWOOBJID_EMPTY, true, false, petSubKey); - + auto* item = inventoryComponent->FindItemBySubKey(petSubKey, MODELS); if (!item) return; @@ -798,35 +760,34 @@ void PetComponent::Wander() { return; } - m_MovementAI->SetMaxSpeed(m_PetInfo.sprintSpeed); //info.wanderSpeed); + m_MovementAI->SetMaxSpeed(m_PetInfo->sprintSpeed); //info.wanderSpeed); m_MovementAI->SetDestination(destination); - m_Timer += (m_MovementAI->GetParent()->GetPosition().x - destination.x) / m_PetInfo.sprintSpeed; + m_Timer += (m_MovementAI->GetParent()->GetPosition().x - destination.x) / m_PetInfo->sprintSpeed; } void PetComponent::OnSpawn() { - if (!LoadPetInfo(m_ComponentId, m_PetInfo)) { - LOG("Failed to load PetComponent (id: %d) information from CDClient!", m_ComponentId); - } + m_PetInfo = CDClientManager::Instance().GetTable()->GetByID(m_ComponentId); + if (!m_PetInfo) LOG("Failed to load PetComponent (id: %d) information from CDClient!", m_ComponentId); + m_MovementAI = m_Parent->GetComponent(); if (m_StartPosition == NiPoint3::ZERO) { m_StartPosition = m_Parent->GetPosition(); } - + if (m_Owner != LWOOBJID_EMPTY) { m_Parent->SetOwnerOverride(m_Owner); - m_MovementAI->SetMaxSpeed(m_PetInfo.sprintSpeed); + m_MovementAI->SetMaxSpeed(m_PetInfo->sprintSpeed); m_MovementAI->SetHaltDistance(m_FollowRadius); //SetOnlyFlag(IDLE); //SetStatus(PetFlag::NONE); SetPetAiState(PetAiState::follow); - } - else { + } else { SetFlag(TAMEABLE); SetPetAiState(PetAiState::idle); } - + SetFlag(IDLE); UnsetFlag(SPAWNING); Game::entityManager->SerializeEntity(m_Parent); @@ -873,8 +834,7 @@ void PetComponent::OnFollow(const float& deltaTime) { // If the player's position is within range, stop moving if (distanceToOwner <= m_FollowRadius * m_FollowRadius) { m_MovementAI->Stop(); - } - else { // Chase the player's new position + } else { // Chase the player's new position m_MovementAI->SetDestination(ownerPos); } @@ -886,8 +846,7 @@ void PetComponent::OnFollow(const float& deltaTime) { m_TimerAway = 0; return; - } - else if (distanceToOwner > 15 * 15 || std::abs(ownerPos.y - currentPos.y) >= 3) { + } else if (distanceToOwner > 15 * 15 || std::abs(ownerPos.y - currentPos.y) >= 3) { m_TimerAway += deltaTime; } @@ -909,17 +868,17 @@ void PetComponent::OnInteract() { } switch (GetInteractType()) { - case PetInteractType::bouncer: - if (IsReadyToInteract()) LOG_DEBUG("Add the HandleInteractBouncer()!"); + case PetInteractType::bouncer: + if (IsReadyToInteract()) HandleInteractBouncer(); else SetupInteractBouncer(); break; - case PetInteractType::treasure: - if (IsReadyToInteract()) HandleInteractTreasureDig(); + case PetInteractType::treasure: + if (IsReadyToInteract()) HandleInteractTreasureDig(); else SetupInteractTreasureDig(); break; - default: + default: LOG_DEBUG("INTERACT = NONE! RETURNING!"); StopInteract(); m_Timer += 0.5f; @@ -932,7 +891,7 @@ void PetComponent::StartInteract(const NiPoint3 position, const PetInteractType SetInteractType(interactType); SetAbility(ePetAbilityType::GoToObject); SetPetAiState(PetAiState::goToObj); - m_MovementAI->SetMaxSpeed(m_PetInfo.runSpeed); + m_MovementAI->SetMaxSpeed(m_PetInfo->runSpeed); m_MovementAI->SetHaltDistance(0.0f); m_MovementAI->SetDestination(position); LOG_DEBUG("Starting interaction!"); @@ -952,7 +911,7 @@ void PetComponent::StopInteract(bool bDontSerialize) { SetOnlyFlag(IDLE); //SetStatus(PetFlag::NONE); SetIsReadyToInteract(false); SetIsHandlingInteraction(false); // Needed? - m_MovementAI->SetMaxSpeed(m_PetInfo.sprintSpeed); + m_MovementAI->SetMaxSpeed(m_PetInfo->sprintSpeed); m_MovementAI->SetHaltDistance(m_FollowRadius); LOG_DEBUG("Stopping interaction!"); @@ -964,44 +923,119 @@ void PetComponent::StopInteract(bool bDontSerialize) { } void PetComponent::SetupInteractBouncer() { + const auto* owner = GetOwner(); + if (!owner) return; + + LOG_DEBUG("Setting up bouncer interaction!"); + SetIsReadyToInteract(true); + const auto petAbility = ePetAbilityType::JumpOnObject; + + SetAbility(petAbility); + UnsetFlag(IDLE); + SetFlag(ON_SWITCH, NOT_WAITING); //SetStatus(PetFlag::NOT_WAITING); // TODO: Double-check this is the right flag being set + LOG_DEBUG("m_Flags = %d", m_Flags); + Game::entityManager->SerializeEntity(m_Parent); // TODO: Double-check pet packet captures + + const auto sysAddr = owner->GetSystemAddress(); + GameMessages::SendHelp(m_Owner, eHelpType::PR_BOUNCER_TUTORIAL_03, sysAddr); + GameMessages::SendShowPetActionButton(m_Owner, petAbility, true, sysAddr); + + SwitchComponent* closestSwitch = SwitchComponent::GetClosestSwitch(m_MovementAI->GetDestination()); // TODO: Find a better way to do this + closestSwitch->EntityEnter(m_Parent); + + m_Timer += 0.5f; +} + +void PetComponent::StartInteractBouncer() { + Entity* user = GetOwner(); + if (IsHandlingInteraction() || !user) return; + + auto* destroyableComponent = user->GetComponent(); + if (!destroyableComponent) return; + + auto imagination = destroyableComponent->GetImagination(); + const int32_t imaginationCost = 2; // TODO: Get rid of this magic number - make static variable from lookup + if (imagination < imaginationCost) { + //GameMessages::SendHelp(user->GetObjectID(), eHelpType::PR_NEED_IMAGINATION, user->GetSystemAddress()); // Check if right message! + return; + } + + GameMessages::SendShowPetActionButton(m_Owner, ePetAbilityType::Invalid, false, user->GetSystemAddress()); + + imagination -= imaginationCost; + destroyableComponent->SetImagination(imagination); + Game::entityManager->SerializeEntity(user); + // THIS IS ALL BAD, BAD, BAD! FIX IT, ME! >:( - /*SetAbility(ePetAbilityType::JumpOnObject); - NiPoint3 destination = m_MovementAI->GetDestination(); - SwitchComponent* closestSwitch = SwitchComponent::GetClosestSwitch(destination); - m_Interaction = closestSwitch->GetParentEntity()->GetObjectID(); - Game::entityManager->SerializeEntity(m_Parent); - closestSwitch->EntityEnter(m_Parent);*/ + SetIsHandlingInteraction(true); + SwitchComponent* closestSwitch = SwitchComponent::GetClosestSwitch(m_MovementAI->GetDestination()); // TODO: Find a better way to do this + closestSwitch->EntityEnter(m_Parent); + + //m_Timer += 0.5; +} + +void PetComponent::HandleInteractBouncer() { + if (IsHandlingInteraction()) { + auto* const owner = GetOwner(); + if (!owner) return; + + auto* const petSwitch = SwitchComponent::GetClosestSwitch(m_MovementAI->GetDestination()); // TODO: Find a better way to do this + if (!petSwitch) return; + + auto* const petSwitchEntity = petSwitch->GetParentEntity(); + if (!petSwitchEntity) return; + + m_Parent->AddCallbackTimer(2.0f, [petSwitch, petSwitchEntity]() { + LOG_DEBUG("Callback start!"); + + petSwitch->GetPetBouncer()->SetPetBouncerEnabled(false); + RenderComponent::PlayAnimation(petSwitchEntity, u"up"); + + LOG_DEBUG("Callback end!"); + }); + + RenderComponent::PlayAnimation(petSwitchEntity, u"launch"); //u"engaged"); + + auto* const petBouncer = petSwitch->GetPetBouncer(); + petBouncer->SetPetBouncerEnabled(true); + + + Command(NiPoint3::ZERO, LWOOBJID_EMPTY, 1, PetEmote::ActivateSwitch, true); // Plays 'jump on switch' animation + StopInteract(); + } + m_Timer += 0.5f; } void PetComponent::SetupInteractTreasureDig() { - auto* owner = GetOwner(); + const auto* owner = GetOwner(); if (!owner) return; LOG_DEBUG("Setting up dig interaction!"); SetIsReadyToInteract(true); - auto petAbility = ePetAbilityType::DigAtPosition; + const auto petAbility = ePetAbilityType::DigAtPosition; SetAbility(petAbility); UnsetFlag(IDLE); - SetFlag(UNKNOWN256, NOT_WAITING); //SetStatus(PetFlag::NOT_WAITING); // TODO: Double-check this is the right flag being set + SetFlag(ON_SWITCH, NOT_WAITING); //SetStatus(PetFlag::NOT_WAITING); // TODO: Double-check this is the right flag being set LOG_DEBUG("m_Flags = %d", m_Flags); Game::entityManager->SerializeEntity(m_Parent); // TODO: Double-check pet packet captures const auto sysAddr = owner->GetSystemAddress(); GameMessages::SendHelp(m_Owner, eHelpType::PR_DIG_TUTORIAL_01, sysAddr); GameMessages::SendShowPetActionButton(m_Owner, petAbility, true, sysAddr); + m_Timer += 0.5f; } void PetComponent::StartInteractTreasureDig() { Entity* user = GetOwner(); if (IsHandlingInteraction() || !user) return; - + auto* destroyableComponent = user->GetComponent(); if (!destroyableComponent) return; - + auto imagination = destroyableComponent->GetImagination(); - int32_t imaginationCost = 1; // TODO: Get rid of this magic number - make static variable from lookup + const int32_t imaginationCost = 1; // TODO: Get rid of this magic number - make static variable from lookup if (imagination < imaginationCost) { //GameMessages::SendHelp(user->GetObjectID(), eHelpType::PR_NEED_IMAGINATION, user->GetSystemAddress()); // Check if right message! return; @@ -1009,12 +1043,12 @@ void PetComponent::StartInteractTreasureDig() { GameMessages::SendShowPetActionButton(m_Owner, ePetAbilityType::Invalid, false, user->GetSystemAddress()); - imagination -= imaginationCost; + imagination -= imaginationCost; destroyableComponent->SetImagination(imagination); Game::entityManager->SerializeEntity(user); SetIsHandlingInteraction(true); - UnsetFlag(UNKNOWN256, NOT_WAITING); // TODO: FIND THE CORRECT STATUS TO USE HERE + UnsetFlag(ON_SWITCH, NOT_WAITING); // TODO: FIND THE CORRECT STATUS TO USE HERE SetFlag(IDLE); LOG_DEBUG("StartInteractTreasureDig() m_Flags = %d", m_Flags); Game::entityManager->SerializeEntity(m_Parent); @@ -1025,10 +1059,10 @@ void PetComponent::StartInteractTreasureDig() { void PetComponent::HandleInteractTreasureDig() { if (IsHandlingInteraction()) { - auto* owner = GetOwner(); + auto* const owner = GetOwner(); if (!owner) return; - - auto* treasure = PetDigServer::GetClosestTresure(m_MovementAI->GetDestination()); // TODO: Find a better way to do this + + auto* const treasure = PetDigServer::GetClosestTresure(m_MovementAI->GetDestination()); // TODO: Find a better way to do this treasure->Smash(m_Parent->GetObjectID()); GameMessages::SendHelp(m_Owner, eHelpType::PR_DIG_TUTORIAL_03, owner->GetSystemAddress()); @@ -1038,7 +1072,7 @@ void PetComponent::HandleInteractTreasureDig() { return; } - + if (m_TimerBounce <= 0.0f) { Command(NiPoint3::ZERO, LWOOBJID_EMPTY, 1, PetEmote::Bounce, true); // Plays 'bounce' animation m_TimerBounce = 1.0f; @@ -1048,6 +1082,9 @@ void PetComponent::HandleInteractTreasureDig() { } void PetComponent::Activate(Item* item, bool registerPet, bool fromTaming) { // TODO: Offset spawn position so it's not on top of player char + m_PetInfo = CDClientManager::Instance().GetTable()->GetByID(m_ComponentId); + if (!m_PetInfo) LOG("Failed to load PetComponent (id: %d) information from CDClient!", m_ComponentId); + AddDrainImaginationTimer(item, fromTaming); m_ItemId = item->GetId(); @@ -1129,22 +1166,22 @@ void PetComponent::AddDrainImaginationTimer(Item* item, bool fromTaming) { if (!fromTaming) playerDestroyableComponent->Imagine(-1); // Set this to a variable so when this is called back from the player the timer doesn't fire off. - m_Parent->AddCallbackTimer(m_PetInfo.imaginationDrainRate, [playerDestroyableComponent, this, item]() { + m_Parent->AddCallbackTimer(m_PetInfo->imaginationDrainRate, [playerDestroyableComponent, this, item]() { if (!playerDestroyableComponent) { LOG("No petComponent and/or no playerDestroyableComponent"); return; } - // If we are out of imagination despawn the pet. - if (playerDestroyableComponent->GetImagination() == 0) { - this->Deactivate(); - auto playerEntity = playerDestroyableComponent->GetParent(); - if (!playerEntity) return; + // If we are out of imagination despawn the pet. + if (playerDestroyableComponent->GetImagination() == 0) { + this->Deactivate(); + auto playerEntity = playerDestroyableComponent->GetParent(); + if (!playerEntity) return; - GameMessages::SendUseItemRequirementsResponse(playerEntity->GetObjectID(), playerEntity->GetSystemAddress(), eUseItemResponse::NoImaginationForPet); - } + GameMessages::SendUseItemRequirementsResponse(playerEntity->GetObjectID(), playerEntity->GetSystemAddress(), eUseItemResponse::NoImaginationForPet); + } - this->AddDrainImaginationTimer(item); + this->AddDrainImaginationTimer(item); }); } @@ -1303,7 +1340,7 @@ void PetComponent::SetPetNameForModeration(const std::string& petName) { } //Save to db: - Database::Get()->SetPetNameModerationStatus(m_DatabaseId, IPetNames::Info{petName, approved}); + Database::Get()->SetPetNameModerationStatus(m_DatabaseId, IPetNames::Info{ petName, approved }); } void PetComponent::LoadPetNameFromModeration() { diff --git a/dGame/dComponents/PetComponent.h b/dGame/dComponents/PetComponent.h index f9b17c680..689db919f 100644 --- a/dGame/dComponents/PetComponent.h +++ b/dGame/dComponents/PetComponent.h @@ -39,9 +39,9 @@ enum PetFlag : uint32_t { UNKNOWN4 = 1 << 2, //0x04 - FOLLOWING(?) BEING_TAMED = 1 << 4, //0x10, NOT_WAITING = 1 << 5, //0x20, - STOP_MOVING = 1 << 6, //0x40 - Seems to be the "stop moving" flag - called when taming begins and stays active until a name is submitted + IMMOBILE = 1 << 6, //0x40 - Seems to be the "stop moving" flag - called when taming begins and stays active until a name is submitted SPAWNING = 1 << 7, //0x80 - UNKNOWN256 = 1 << 8, //0x100 + ON_SWITCH = 1 << 8, //0x100 UNKNOWN1024 = 1 << 10, //0x400 TAMEABLE = 1 << 26 //0x4000000 }; @@ -69,11 +69,6 @@ class PetComponent : public Component { PetComponent(Entity* parentEntity, uint32_t componentId); ~PetComponent() override; - - /** - * Loads pet info from CDClient - */ - bool LoadPetInfo(uint32_t petId, CDPetComponent& result); /** * Serializes the pet @@ -346,6 +341,16 @@ class PetComponent : public Component { */ void SetupInteractBouncer(); + /** + * Starts the pet bouncer interaction + */ + void StartInteractBouncer(); + + /** + * Handles the pet bouncer interaction + */ + void HandleInteractBouncer(); + /** * Set up the treasure dig interaction */ @@ -437,7 +442,7 @@ class PetComponent : public Component { /** * Pet information loaded from the CDClientDatabase */ - CDPetComponent m_PetInfo; + CDPetComponent* m_PetInfo; /** * Cache of all the pets that are currently spawned, indexed by tamer diff --git a/dGame/dComponents/SwitchComponent.cpp b/dGame/dComponents/SwitchComponent.cpp index 41dff9f5d..3d9e0218e 100644 --- a/dGame/dComponents/SwitchComponent.cpp +++ b/dGame/dComponents/SwitchComponent.cpp @@ -60,8 +60,8 @@ void SwitchComponent::EntityEnter(Entity* entity) { if (m_PetBouncer != nullptr) { GameMessages::SendPlayFXEffect(m_Parent->GetObjectID(), 2602, u"pettriggeractive", "BounceEffect", LWOOBJID_EMPTY, 1, 1, true); - RenderComponent::PlayAnimation(m_Parent, u"engaged"); - m_PetBouncer->SetPetBouncerEnabled(true); + //RenderComponent::PlayAnimation(m_Parent, u"engaged"); + //m_PetBouncer->SetPetBouncerEnabled(true); } else { Game::entityManager->SerializeEntity(m_Parent); } From 5f8f200bfcdc829b2f597798bc71be4a97211462 Mon Sep 17 00:00:00 2001 From: jadebenn Date: Thu, 28 Dec 2023 17:49:37 -0600 Subject: [PATCH 45/73] pet bouncers now bounce player upon pressing shift --- dGame/dComponents/PetComponent.cpp | 22 +++++++++++++++------ dGame/dComponents/PetComponent.h | 14 +++++++------- dGame/dGameMessages/GameMessages.cpp | 29 ++++++++++++++++++++++++++++ dGame/dGameMessages/GameMessages.h | 18 +++++++++++++++++ 4 files changed, 70 insertions(+), 13 deletions(-) diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index 1d8602f34..ed0f73f8f 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -886,7 +886,7 @@ void PetComponent::OnInteract() { } } -void PetComponent::StartInteract(const NiPoint3 position, const PetInteractType interactType, const LWOOBJID interactID) { +void PetComponent::StartInteract(const NiPoint3& position, const PetInteractType interactType, const LWOOBJID& interactID) { SetInteraction(interactID); // TODO: Check if this should be serialized for goToObj SetInteractType(interactType); SetAbility(ePetAbilityType::GoToObject); @@ -978,6 +978,7 @@ void PetComponent::HandleInteractBouncer() { if (IsHandlingInteraction()) { auto* const owner = GetOwner(); if (!owner) return; + const auto sysAddr = owner->GetSystemAddress(); auto* const petSwitch = SwitchComponent::GetClosestSwitch(m_MovementAI->GetDestination()); // TODO: Find a better way to do this if (!petSwitch) return; @@ -985,16 +986,25 @@ void PetComponent::HandleInteractBouncer() { auto* const petSwitchEntity = petSwitch->GetParentEntity(); if (!petSwitchEntity) return; - m_Parent->AddCallbackTimer(2.0f, [petSwitch, petSwitchEntity]() { + m_Parent->AddCallbackTimer(1.0f, [this, petSwitch, petSwitchEntity, sysAddr]() { LOG_DEBUG("Callback start!"); - petSwitch->GetPetBouncer()->SetPetBouncerEnabled(false); + const auto bouncerComp = petSwitch->GetPetBouncer(); + const auto bouncerCompPos = bouncerComp->GetParentEntity()->GetPosition(); + const auto bouncerId = bouncerComp->GetParentEntity()->GetObjectID(); + + const auto petId = this->GetParentEntity()->GetObjectID(); + + RenderComponent::PlayAnimation(petSwitchEntity, u"launch"); //u"engaged"); + bouncerComp->SetPetBouncerEnabled(true); + GameMessages::SendRequestClientBounce(bouncerId, this->GetOwnerId(), NiPoint3::ZERO, NiPoint3::ZERO, bouncerId, true, false, UNASSIGNED_SYSTEM_ADDRESS); //TODO: Check packet captures!! + bouncerComp->SetPetBouncerEnabled(false); RenderComponent::PlayAnimation(petSwitchEntity, u"up"); LOG_DEBUG("Callback end!"); - }); + }); - RenderComponent::PlayAnimation(petSwitchEntity, u"launch"); //u"engaged"); + //RenderComponent::PlayAnimation(petSwitchEntity, u"launch"); //u"engaged"); auto* const petBouncer = petSwitch->GetPetBouncer(); petBouncer->SetPetBouncerEnabled(true); @@ -1223,7 +1233,7 @@ void PetComponent::Release() { item->SetCount(0, false, false); } -void PetComponent::Command(NiPoint3 position, LWOOBJID source, int32_t commandType, int32_t typeId, bool overrideObey) { +void PetComponent::Command(const NiPoint3& position, const LWOOBJID& source, int32_t commandType, int32_t typeId, bool overrideObey) { auto* owner = GetOwner(); if (!owner) return; diff --git a/dGame/dComponents/PetComponent.h b/dGame/dComponents/PetComponent.h index 689db919f..f80082315 100644 --- a/dGame/dComponents/PetComponent.h +++ b/dGame/dComponents/PetComponent.h @@ -93,35 +93,35 @@ class PetComponent : public Component { * @param flag PetFlag(s) to set */ template - void SetFlag(varArg... flag) { m_Flags |= (static_cast(flag) | ...); }; + inline void SetFlag(varArg... flag) { m_Flags |= (static_cast(flag) | ...); }; /** * Sets the pet to ONLY have the specified flag(s), clearing all others * @param flag PetFlag(s) to set exclusively */ template - void SetOnlyFlag(varArg... flag) { m_Flags = (static_cast(flag) | ...); }; + inline void SetOnlyFlag(varArg... flag) { m_Flags = (static_cast(flag) | ...); }; /** * Unsets one or more pet flags * @param flag PetFlag(s) to unset */ template - void UnsetFlag(varArg... flag) { m_Flags &= ~(static_cast(flag) | ...); }; + inline void UnsetFlag(varArg... flag) { m_Flags &= ~(static_cast(flag) | ...); }; /** * Returns true if the pet has all the specified flag(s) * @param flag PetFlag(s) to check */ template - const bool HasFlag(varArg... flag) { return (m_Flags & (static_cast(flag) | ...)) == (static_cast(flag) | ...); }; + inline constexpr bool HasFlag(varArg... flag) { return (m_Flags & (static_cast(flag) | ...)) == (static_cast(flag) | ...); }; /** * Returns true if the pet has ONLY the specified flag(s) * @param flag PetFlag(s) to check if the pet has exclusively */ template - const bool HasOnlyFlag(varArg... flag) { return m_Flags == (static_cast(flag) | ...); }; + inline constexpr bool HasOnlyFlag(varArg... flag) { return m_Flags == (static_cast(flag) | ...); }; /** * Governs the pet update loop @@ -202,7 +202,7 @@ class PetComponent : public Component { /** * Start a pet interaction with an object at a given position */ - void StartInteract(const NiPoint3 position, const PetInteractType interactType, const LWOOBJID interactID); + void StartInteract(const NiPoint3& position, const PetInteractType interactType, const LWOOBJID& interactID); /** * Stop a pet interaction with an object @@ -245,7 +245,7 @@ class PetComponent : public Component { * @param typeId extra information about the command, e.g. the emote to play * @param overrideObey unused */ - void Command(NiPoint3 position, LWOOBJID source, int32_t commandType, int32_t typeId, bool overrideObey); + void Command(const NiPoint3& position, const LWOOBJID& source, int32_t commandType, int32_t typeId, bool overrideObey); /** * Returns the ID of the owner of this pet (if any) diff --git a/dGame/dGameMessages/GameMessages.cpp b/dGame/dGameMessages/GameMessages.cpp index 2151005a6..fd3340973 100644 --- a/dGame/dGameMessages/GameMessages.cpp +++ b/dGame/dGameMessages/GameMessages.cpp @@ -3589,6 +3589,35 @@ void GameMessages::SendBouncerActiveStatus(LWOOBJID objectId, bool bActive, cons SEND_PACKET; } +void GameMessages::SendRequestClientBounce(const LWOOBJID& objectId, const LWOOBJID& bounceTargetId, const NiPoint3& bounceTargetPos, const NiPoint3& bouncedObjLinVel, const LWOOBJID& requestSourceId, const bool bAllBounced, const bool bAllowClientOverload, const SystemAddress& sysAddr) { + LOG_DEBUG("REQUEST CLIENT BOUNCE!"); + + CBITSTREAM; + CMSGHEADER; + + bitStream.Write(objectId); + LOG_DEBUG("Object id: %llu", objectId); + bitStream.Write(eGameMessageType::REQUEST_CLIENT_BOUNCE); + + bitStream.Write(bounceTargetId); + LOG_DEBUG("Bounce target id: %llu", bounceTargetId); + bitStream.Write(bounceTargetPos.x); + bitStream.Write(bounceTargetPos.y); + bitStream.Write(bounceTargetPos.z); + LOG_DEBUG("Bounce target pos on server: %f %f %f", bounceTargetPos.x, bounceTargetPos.y, bounceTargetPos.z); + bitStream.Write(bouncedObjLinVel.x); + bitStream.Write(bouncedObjLinVel.y); + bitStream.Write(bouncedObjLinVel.z); + LOG_DEBUG("Bounced object linear velocity: %f %f %f", bouncedObjLinVel.x, bouncedObjLinVel.y, bouncedObjLinVel.z); + bitStream.Write(requestSourceId); + LOG_DEBUG("Request source id: %llu", requestSourceId); + + bitStream.Write(bAllBounced); + bitStream.Write(bAllowClientOverload); + + if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST; + SEND_PACKET; +} void GameMessages::SendSetPetName(LWOOBJID objectId, std::u16string name, LWOOBJID petDBID, const SystemAddress& sysAddr) { CBITSTREAM; diff --git a/dGame/dGameMessages/GameMessages.h b/dGame/dGameMessages/GameMessages.h index 132a61436..bd8982999 100644 --- a/dGame/dGameMessages/GameMessages.h +++ b/dGame/dGameMessages/GameMessages.h @@ -393,8 +393,26 @@ namespace GameMessages { void SendPlayEmote(LWOOBJID objectId, int32_t emoteID, LWOOBJID target, const SystemAddress& sysAddr); + /** + * Sends the bouncer active status + * @param objectId Object ID of the bouncer + * @param bActive Active state of the bouncer + * @param sysAddr System address of the user + */ void SendBouncerActiveStatus(LWOOBJID objectId, bool bActive, const SystemAddress& sysAddr); + /** + * Sends a request to the client to bounce (I think?) + * @param objectId Object ID + * @param bounceTargetId The object ID of the bounce target + * @param bounceTargetPos The position of the bounce target + * @param bouncedObjLinVel TODO: UNUSED + * @param requestSourceId The object Id of the entity requesting the bounce + * @param bAllBounced Whether to bounce all entities standing on the bouncer pad + * @param bAllowClientOverload TODO: UNUSED + */ + void SendRequestClientBounce(const LWOOBJID& objectId, const LWOOBJID& bounceTargetId, const NiPoint3& bounceTargetPos, const NiPoint3& bouncedObjLinVel, const LWOOBJID& requestSourceId, const bool bAllBounced, const bool bAllowClientOverload, const SystemAddress& sysAddr); + void SendSetPetName(LWOOBJID objectId, std::u16string name, LWOOBJID petDBID, const SystemAddress& sysAddr); void SendSetPetNameModerated(LWOOBJID objectId, LWOOBJID petDBID, int32_t nModerationStatus, const SystemAddress& sysAddr); From b83e1bf20d98f9130674186a76d7050455baf787 Mon Sep 17 00:00:00 2001 From: jadebenn Date: Thu, 28 Dec 2023 18:21:45 -0600 Subject: [PATCH 46/73] some further bouncer refinements --- dGame/dComponents/PetComponent.cpp | 22 ++++------------------ dGame/dGameMessages/GameMessages.cpp | 2 -- 2 files changed, 4 insertions(+), 20 deletions(-) diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index ed0f73f8f..2bb9f0e64 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -976,39 +976,25 @@ void PetComponent::StartInteractBouncer() { void PetComponent::HandleInteractBouncer() { if (IsHandlingInteraction()) { - auto* const owner = GetOwner(); - if (!owner) return; - const auto sysAddr = owner->GetSystemAddress(); - auto* const petSwitch = SwitchComponent::GetClosestSwitch(m_MovementAI->GetDestination()); // TODO: Find a better way to do this if (!petSwitch) return; auto* const petSwitchEntity = petSwitch->GetParentEntity(); if (!petSwitchEntity) return; - m_Parent->AddCallbackTimer(1.0f, [this, petSwitch, petSwitchEntity, sysAddr]() { - LOG_DEBUG("Callback start!"); - - const auto bouncerComp = petSwitch->GetPetBouncer(); + m_Parent->AddCallbackTimer(1.0f, [this, petSwitch, petSwitchEntity]() { + auto* const bouncerComp = petSwitch->GetPetBouncer(); const auto bouncerCompPos = bouncerComp->GetParentEntity()->GetPosition(); const auto bouncerId = bouncerComp->GetParentEntity()->GetObjectID(); - const auto petId = this->GetParentEntity()->GetObjectID(); - - RenderComponent::PlayAnimation(petSwitchEntity, u"launch"); //u"engaged"); bouncerComp->SetPetBouncerEnabled(true); GameMessages::SendRequestClientBounce(bouncerId, this->GetOwnerId(), NiPoint3::ZERO, NiPoint3::ZERO, bouncerId, true, false, UNASSIGNED_SYSTEM_ADDRESS); //TODO: Check packet captures!! bouncerComp->SetPetBouncerEnabled(false); RenderComponent::PlayAnimation(petSwitchEntity, u"up"); - - LOG_DEBUG("Callback end!"); }); - //RenderComponent::PlayAnimation(petSwitchEntity, u"launch"); //u"engaged"); - - auto* const petBouncer = petSwitch->GetPetBouncer(); - petBouncer->SetPetBouncerEnabled(true); - + RenderComponent::PlayAnimation(petSwitchEntity, u"launch"); //u"engaged"); //TODO: Check if the timing on this is right + // TODO: Need to freeze player movement until the bounce begins! Command(NiPoint3::ZERO, LWOOBJID_EMPTY, 1, PetEmote::ActivateSwitch, true); // Plays 'jump on switch' animation StopInteract(); diff --git a/dGame/dGameMessages/GameMessages.cpp b/dGame/dGameMessages/GameMessages.cpp index fd3340973..c98844a04 100644 --- a/dGame/dGameMessages/GameMessages.cpp +++ b/dGame/dGameMessages/GameMessages.cpp @@ -3590,8 +3590,6 @@ void GameMessages::SendBouncerActiveStatus(LWOOBJID objectId, bool bActive, cons } void GameMessages::SendRequestClientBounce(const LWOOBJID& objectId, const LWOOBJID& bounceTargetId, const NiPoint3& bounceTargetPos, const NiPoint3& bouncedObjLinVel, const LWOOBJID& requestSourceId, const bool bAllBounced, const bool bAllowClientOverload, const SystemAddress& sysAddr) { - LOG_DEBUG("REQUEST CLIENT BOUNCE!"); - CBITSTREAM; CMSGHEADER; From 1ee5b64accc218c51873f9a5cb92196bf6161f57 Mon Sep 17 00:00:00 2001 From: jadebenn Date: Mon, 1 Jan 2024 03:24:00 -0600 Subject: [PATCH 47/73] Update PetComponent.h --- dGame/dComponents/PetComponent.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dGame/dComponents/PetComponent.h b/dGame/dComponents/PetComponent.h index f80082315..6c0b00540 100644 --- a/dGame/dComponents/PetComponent.h +++ b/dGame/dComponents/PetComponent.h @@ -114,14 +114,14 @@ class PetComponent : public Component { * @param flag PetFlag(s) to check */ template - inline constexpr bool HasFlag(varArg... flag) { return (m_Flags & (static_cast(flag) | ...)) == (static_cast(flag) | ...); }; + inline const bool HasFlag(varArg... flag) { return (m_Flags & (static_cast(flag) | ...)) == (static_cast(flag) | ...); }; /** * Returns true if the pet has ONLY the specified flag(s) * @param flag PetFlag(s) to check if the pet has exclusively */ template - inline constexpr bool HasOnlyFlag(varArg... flag) { return m_Flags == (static_cast(flag) | ...); }; + inline const bool HasOnlyFlag(varArg... flag) { return m_Flags == (static_cast(flag) | ...); }; /** * Governs the pet update loop From 17753c56e4f4d6b3a280911ce91806e10f6aa99e Mon Sep 17 00:00:00 2001 From: jadebenn Date: Mon, 1 Jan 2024 12:21:49 -0600 Subject: [PATCH 48/73] Update PetComponent.h --- dGame/dComponents/PetComponent.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/dGame/dComponents/PetComponent.h b/dGame/dComponents/PetComponent.h index 6c0b00540..f82cb132e 100644 --- a/dGame/dComponents/PetComponent.h +++ b/dGame/dComponents/PetComponent.h @@ -93,35 +93,35 @@ class PetComponent : public Component { * @param flag PetFlag(s) to set */ template - inline void SetFlag(varArg... flag) { m_Flags |= (static_cast(flag) | ...); }; + void SetFlag(varArg... flag) { m_Flags |= (static_cast(flag) | ...); }; /** * Sets the pet to ONLY have the specified flag(s), clearing all others * @param flag PetFlag(s) to set exclusively */ template - inline void SetOnlyFlag(varArg... flag) { m_Flags = (static_cast(flag) | ...); }; + void SetOnlyFlag(varArg... flag) { m_Flags = (static_cast(flag) | ...); }; /** * Unsets one or more pet flags * @param flag PetFlag(s) to unset */ template - inline void UnsetFlag(varArg... flag) { m_Flags &= ~(static_cast(flag) | ...); }; + void UnsetFlag(varArg... flag) { m_Flags &= ~(static_cast(flag) | ...); }; /** * Returns true if the pet has all the specified flag(s) * @param flag PetFlag(s) to check */ template - inline const bool HasFlag(varArg... flag) { return (m_Flags & (static_cast(flag) | ...)) == (static_cast(flag) | ...); }; + const bool HasFlag(varArg... flag) { return (m_Flags & (static_cast(flag) | ...)) == (static_cast(flag) | ...); }; /** * Returns true if the pet has ONLY the specified flag(s) * @param flag PetFlag(s) to check if the pet has exclusively */ template - inline const bool HasOnlyFlag(varArg... flag) { return m_Flags == (static_cast(flag) | ...); }; + const bool HasOnlyFlag(varArg... flag) { return m_Flags == (static_cast(flag) | ...); }; /** * Governs the pet update loop @@ -192,7 +192,7 @@ class PetComponent : public Component { /** * Continues a step in the follow state, making sure that the entity is around its start position */ - void OnFollow(const float& deltaTime); + void OnFollow(const float deltaTime); /** * Continues a step in the interact state, handling the pet's interaction with an entity From d5a0b71b9ecb813e87898dea4e462a4aa3c71353 Mon Sep 17 00:00:00 2001 From: jadebenn Date: Mon, 1 Jan 2024 12:24:13 -0600 Subject: [PATCH 49/73] Update PetComponent.cpp --- dGame/dComponents/PetComponent.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index 2bb9f0e64..d235b7b5e 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -793,7 +793,7 @@ void PetComponent::OnSpawn() { Game::entityManager->SerializeEntity(m_Parent); } -void PetComponent::OnFollow(const float& deltaTime) { +void PetComponent::OnFollow(const float deltaTime) { Entity* owner = GetOwner(); if (!owner) return; From 384083ea1809587f8352522c9c1bf678f669d9c3 Mon Sep 17 00:00:00 2001 From: jadebenn Date: Fri, 5 Jan 2024 14:50:49 -0600 Subject: [PATCH 50/73] use reference instead of pointer for cdclient data --- .../CDClientTables/CDPetComponentTable.cpp | 5 +- .../CDClientTables/CDPetComponentTable.h | 2 +- dGame/dComponents/PetComponent.cpp | 56 +++---------------- dGame/dComponents/PetComponent.h | 51 ++++++++++++++--- 4 files changed, 55 insertions(+), 59 deletions(-) diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDPetComponentTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDPetComponentTable.cpp index 03bf479d4..754e7b7ce 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDPetComponentTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDPetComponentTable.cpp @@ -28,7 +28,8 @@ void CDPetComponentTable::LoadValuesFromDatabase() { } -CDPetComponent* CDPetComponentTable::GetByID(unsigned int componentID) { +CDPetComponent& CDPetComponentTable::GetByID(unsigned int componentID) { auto itr = m_entries.find(componentID); - return itr != m_entries.end() ? &itr->second : nullptr; + if (itr == m_entries.end()) throw std::exception(); // TODO: Use a default set of values instead? + return itr->second; } diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDPetComponentTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDPetComponentTable.h index 5e0d6477b..bb37edab7 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDPetComponentTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDPetComponentTable.h @@ -25,7 +25,7 @@ class CDPetComponentTable : public CDTable { void LoadValuesFromDatabase(); static const std::string GetTableName() { return "PetComponent"; }; - CDPetComponent* GetByID(unsigned int componentID); + CDPetComponent& GetByID(unsigned int componentID); private: std::map m_entries; diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index d235b7b5e..2f0478add 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -35,9 +35,6 @@ #include "eGameMasterLevel.h" #include "eMissionState.h" -#define START_BITMASK_SWITCH(x) \ - for (uint32_t bit = 1; x >= bit; bit *= 2) if (x & bit) switch (bit) - std::unordered_map PetComponent::buildCache{}; std::unordered_map PetComponent::currentActivities{}; std::unordered_map PetComponent::activePets{}; @@ -76,39 +73,6 @@ std::map PetComponent::petFlags = { { 13067, 838 }, // Skeleton dragon }; -PetComponent::PetComponent(Entity* parent, uint32_t componentId) : Component(parent) { - m_ComponentId = componentId; - - m_Interaction = LWOOBJID_EMPTY; - m_InteractType = PetInteractType::none; - m_Owner = LWOOBJID_EMPTY; - m_ModerationStatus = 0; - m_Tamer = LWOOBJID_EMPTY; - m_ModelId = LWOOBJID_EMPTY; - m_Timer = 0; - m_TimerAway = 0; - m_TimerBounce = 0; - m_DatabaseId = LWOOBJID_EMPTY; - m_Flags = PetFlag::SPAWNING; // Tameable - m_Ability = ePetAbilityType::Invalid; - m_StartPosition = m_Parent->GetPosition(); //NiPoint3::ZERO; - m_MovementAI = nullptr; - m_Preconditions = nullptr; - - m_ReadyToInteract = false; - SetPetAiState(PetAiState::spawn); - SetIsHandlingInteraction(false); - - std::string checkPreconditions = GeneralUtils::UTF16ToWTF8(parent->GetVar(u"CheckPrecondition")); - - if (!checkPreconditions.empty()) { - SetPreconditions(checkPreconditions); - } - - // Load database values - m_FollowRadius = Game::zoneManager->GetPetFollowRadius(); -} - void PetComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) { const bool tamed = m_Owner != LWOOBJID_EMPTY; @@ -760,17 +724,14 @@ void PetComponent::Wander() { return; } - m_MovementAI->SetMaxSpeed(m_PetInfo->sprintSpeed); //info.wanderSpeed); + m_MovementAI->SetMaxSpeed(m_PetInfo.sprintSpeed); //info.wanderSpeed); m_MovementAI->SetDestination(destination); - m_Timer += (m_MovementAI->GetParent()->GetPosition().x - destination.x) / m_PetInfo->sprintSpeed; + m_Timer += (m_MovementAI->GetParent()->GetPosition().x - destination.x) / m_PetInfo.sprintSpeed; } void PetComponent::OnSpawn() { - m_PetInfo = CDClientManager::Instance().GetTable()->GetByID(m_ComponentId); - if (!m_PetInfo) LOG("Failed to load PetComponent (id: %d) information from CDClient!", m_ComponentId); - m_MovementAI = m_Parent->GetComponent(); if (m_StartPosition == NiPoint3::ZERO) { @@ -779,7 +740,7 @@ void PetComponent::OnSpawn() { if (m_Owner != LWOOBJID_EMPTY) { m_Parent->SetOwnerOverride(m_Owner); - m_MovementAI->SetMaxSpeed(m_PetInfo->sprintSpeed); + m_MovementAI->SetMaxSpeed(m_PetInfo.sprintSpeed); m_MovementAI->SetHaltDistance(m_FollowRadius); //SetOnlyFlag(IDLE); //SetStatus(PetFlag::NONE); SetPetAiState(PetAiState::follow); @@ -891,7 +852,7 @@ void PetComponent::StartInteract(const NiPoint3& position, const PetInteractType SetInteractType(interactType); SetAbility(ePetAbilityType::GoToObject); SetPetAiState(PetAiState::goToObj); - m_MovementAI->SetMaxSpeed(m_PetInfo->runSpeed); + m_MovementAI->SetMaxSpeed(m_PetInfo.runSpeed); m_MovementAI->SetHaltDistance(0.0f); m_MovementAI->SetDestination(position); LOG_DEBUG("Starting interaction!"); @@ -911,7 +872,7 @@ void PetComponent::StopInteract(bool bDontSerialize) { SetOnlyFlag(IDLE); //SetStatus(PetFlag::NONE); SetIsReadyToInteract(false); SetIsHandlingInteraction(false); // Needed? - m_MovementAI->SetMaxSpeed(m_PetInfo->sprintSpeed); + m_MovementAI->SetMaxSpeed(m_PetInfo.sprintSpeed); m_MovementAI->SetHaltDistance(m_FollowRadius); LOG_DEBUG("Stopping interaction!"); @@ -991,7 +952,7 @@ void PetComponent::HandleInteractBouncer() { GameMessages::SendRequestClientBounce(bouncerId, this->GetOwnerId(), NiPoint3::ZERO, NiPoint3::ZERO, bouncerId, true, false, UNASSIGNED_SYSTEM_ADDRESS); //TODO: Check packet captures!! bouncerComp->SetPetBouncerEnabled(false); RenderComponent::PlayAnimation(petSwitchEntity, u"up"); - }); + }); RenderComponent::PlayAnimation(petSwitchEntity, u"launch"); //u"engaged"); //TODO: Check if the timing on this is right // TODO: Need to freeze player movement until the bounce begins! @@ -1078,9 +1039,6 @@ void PetComponent::HandleInteractTreasureDig() { } void PetComponent::Activate(Item* item, bool registerPet, bool fromTaming) { // TODO: Offset spawn position so it's not on top of player char - m_PetInfo = CDClientManager::Instance().GetTable()->GetByID(m_ComponentId); - if (!m_PetInfo) LOG("Failed to load PetComponent (id: %d) information from CDClient!", m_ComponentId); - AddDrainImaginationTimer(item, fromTaming); m_ItemId = item->GetId(); @@ -1162,7 +1120,7 @@ void PetComponent::AddDrainImaginationTimer(Item* item, bool fromTaming) { if (!fromTaming) playerDestroyableComponent->Imagine(-1); // Set this to a variable so when this is called back from the player the timer doesn't fire off. - m_Parent->AddCallbackTimer(m_PetInfo->imaginationDrainRate, [playerDestroyableComponent, this, item]() { + m_Parent->AddCallbackTimer(m_PetInfo.imaginationDrainRate, [playerDestroyableComponent, this, item]() { if (!playerDestroyableComponent) { LOG("No petComponent and/or no playerDestroyableComponent"); return; diff --git a/dGame/dComponents/PetComponent.h b/dGame/dComponents/PetComponent.h index f82cb132e..fb9d7f114 100644 --- a/dGame/dComponents/PetComponent.h +++ b/dGame/dComponents/PetComponent.h @@ -8,6 +8,7 @@ #include "eReplicaComponentType.h" #include "ePetAbilityType.h" #include "CDPetComponentTable.h" +#include "CDClientManager.h" /* * The current state of the pet AI @@ -67,9 +68,50 @@ class PetComponent : public Component { public: inline static const eReplicaComponentType ComponentType = eReplicaComponentType::PET; - PetComponent(Entity* parentEntity, uint32_t componentId); + /** + * Pet information loaded from the CDClientDatabase + */ + CDPetComponent& m_PetInfo; + + /** + * PetComponent constructor + * @param parentEntity The parent entity + * @param componentId The component id + */ + explicit PetComponent(Entity* parentEntity, uint32_t componentId) : Component{ parentEntity }, + m_PetInfo{ CDClientManager::Instance().GetTable()->GetByID(componentId) } { + m_ComponentId = componentId; + m_Interaction = LWOOBJID_EMPTY; + m_InteractType = PetInteractType::none; + m_Owner = LWOOBJID_EMPTY; + m_ModerationStatus = 0; + m_Tamer = LWOOBJID_EMPTY; + m_ModelId = LWOOBJID_EMPTY; + m_Timer = 0; + m_TimerAway = 0; + m_TimerBounce = 0; + m_DatabaseId = LWOOBJID_EMPTY; + m_Flags = PetFlag::SPAWNING; // Tameable + m_Ability = ePetAbilityType::Invalid; + m_StartPosition = m_Parent->GetPosition(); + m_MovementAI = nullptr; + m_Preconditions = nullptr; + + m_ReadyToInteract = false; + SetPetAiState(PetAiState::spawn); + SetIsHandlingInteraction(false); + + std::string checkPreconditions = GeneralUtils::UTF16ToWTF8(parentEntity->GetVar(u"CheckPrecondition")); + + if (!checkPreconditions.empty()) { + SetPreconditions(checkPreconditions); + } + + m_FollowRadius = 8.0f; //Game::zoneManager->GetPetFollowRadius(); // TODO: FIX THIS TO LOAD DYNAMICALLY + } + ~PetComponent() override; - + /** * Serializes the pet * @param outBitStream The output bitstream @@ -439,11 +481,6 @@ class PetComponent : public Component { int32_t numValidPieces; }; - /** - * Pet information loaded from the CDClientDatabase - */ - CDPetComponent* m_PetInfo; - /** * Cache of all the pets that are currently spawned, indexed by tamer */ From 6c3c08cd2a6193eb8697ea10870d795e8414900d Mon Sep 17 00:00:00 2001 From: jadebenn Date: Fri, 5 Jan 2024 16:36:02 -0600 Subject: [PATCH 51/73] Added fallback to default values if CDClient data cannot be loaded --- .../CDClientTables/CDPetComponentTable.cpp | 54 +++++++++++++------ .../CDClientDatabase/CDClientTables/CDTable.h | 3 ++ dGame/dComponents/PetComponent.cpp | 32 +++++++++++ dGame/dComponents/PetComponent.h | 44 +++------------ 4 files changed, 81 insertions(+), 52 deletions(-) diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDPetComponentTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDPetComponentTable.cpp index 754e7b7ce..151e5156f 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDPetComponentTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDPetComponentTable.cpp @@ -1,24 +1,45 @@ #include "CDPetComponentTable.h" +namespace { + // Default entries for fallback + CDPetComponent defaultEntry { + .id = static_cast(-1), + UNUSED_DEFAULT(.minTameUpdateTime = 60.0f,) + UNUSED_DEFAULT(.maxTameUpdateTime = 300.0f,) + UNUSED_DEFAULT(.percentTameChance = 101.0f,) + UNUSED_DEFAULT(.tameability = 100.0f,) + UNUSED_DEFAULT(.elementType = 1,) + .walkSpeed = 2.5f, + .runSpeed = 5.0f, + .sprintSpeed = 10.0f, + UNUSED_DEFAULT(.idleTimeMin = 60.0f,) + UNUSED_DEFAULT(.idleTimeMax = 300.0f,) + UNUSED_DEFAULT(.petForm = 0,) + .imaginationDrainRate = 60.0f, + UNUSED_DEFAULT(.AudioMetaEventSet = "",) + UNUSED_DEFAULT(.buffIDs = "",) + }; +} + void CDPetComponentTable::LoadValuesFromDatabase() { auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM PetComponent"); while (!tableData.eof()) { CDPetComponent entry; - entry.id = tableData.getIntField("id", -1); - UNUSED_COLUMN(entry.minTameUpdateTime = tableData.getFloatField("minTameUpdateTime", 60.0f);) - UNUSED_COLUMN(entry.maxTameUpdateTime = tableData.getFloatField("maxTameUpdateTime", 300.0f);) - UNUSED_COLUMN(entry.percentTameChance = tableData.getFloatField("percentTameChance", 101.0f);) - UNUSED_COLUMN(entry.tameability = tableData.getFloatField("tamability", 100.0f);) // Mispelled as "tamability" in CDClient - UNUSED_COLUMN(entry.elementType = tableData.getIntField("elementType", 1);) - entry.walkSpeed = tableData.getFloatField("walkSpeed", 2.5f); - entry.runSpeed = tableData.getFloatField("runSpeed", 5.0f); - entry.sprintSpeed = tableData.getFloatField("sprintSpeed", 10.0f); - UNUSED_COLUMN(entry.idleTimeMin = tableData.getFloatField("idleTimeMin", 60.0f);) - UNUSED_COLUMN(entry.idleTimeMax = tableData.getFloatField("idleTimeMax", 300.0f);) + entry.id = tableData.getIntField("id", defaultEntry.id); + UNUSED_COLUMN(entry.minTameUpdateTime = tableData.getFloatField("minTameUpdateTime", defaultEntry.minTameUpdateTime);) + UNUSED_COLUMN(entry.maxTameUpdateTime = tableData.getFloatField("maxTameUpdateTime", defaultEntry.maxTameUpdateTime);) + UNUSED_COLUMN(entry.percentTameChance = tableData.getFloatField("percentTameChance", defaultEntry.percentTameChance);) + UNUSED_COLUMN(entry.tameability = tableData.getFloatField("tamability", defaultEntry.tameability);) // Mispelled as "tamability" in CDClient + UNUSED_COLUMN(entry.elementType = tableData.getIntField("elementType", defaultEntry.elementType);) + entry.walkSpeed = tableData.getFloatField("walkSpeed", defaultEntry.walkSpeed); + entry.runSpeed = tableData.getFloatField("runSpeed", defaultEntry.runSpeed); + entry.sprintSpeed = tableData.getFloatField("sprintSpeed", defaultEntry.sprintSpeed); + UNUSED_COLUMN(entry.idleTimeMin = tableData.getFloatField("idleTimeMin", defaultEntry.idleTimeMin);) + UNUSED_COLUMN(entry.idleTimeMax = tableData.getFloatField("idleTimeMax", defaultEntry.idleTimeMax);) UNUSED_COLUMN(entry.petForm = tableData.getIntField("petForm", 0);) - entry.imaginationDrainRate = tableData.getFloatField("imaginationDrainRate", 60.0f); - UNUSED_COLUMN(entry.AudioMetaEventSet = tableData.getStringField("AudioMetaEventSet", "");) - UNUSED_COLUMN(entry.buffIDs = tableData.getStringField("buffIDs", "");) + entry.imaginationDrainRate = tableData.getFloatField("imaginationDrainRate", defaultEntry.imaginationDrainRate); + UNUSED_COLUMN(entry.AudioMetaEventSet = tableData.getStringField("AudioMetaEventSet", defaultEntry.AudioMetaEventSet);) + UNUSED_COLUMN(entry.buffIDs = tableData.getStringField("buffIDs", defaultEntry.buffIDs);) m_entries.insert(std::make_pair(entry.id, entry)); tableData.nextRow(); @@ -30,6 +51,9 @@ void CDPetComponentTable::LoadValuesFromDatabase() { CDPetComponent& CDPetComponentTable::GetByID(unsigned int componentID) { auto itr = m_entries.find(componentID); - if (itr == m_entries.end()) throw std::exception(); // TODO: Use a default set of values instead? + if (itr == m_entries.end()) { + LOG("Unable to load pet component (ID %i) values from database! Using default values instead.", componentID); + return defaultEntry; + } return itr->second; } diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDTable.h index 0a8f29adb..3569a8614 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDTable.h @@ -23,6 +23,9 @@ // Enable this to skip some unused columns in some tables #define UNUSED_COLUMN(v) +// Use this to skip unused defaults for unused columns in some tables +#define UNUSED_DEFAULT(v, x) + #pragma warning (disable : 4244) //Disable double to float conversion warnings #pragma warning (disable : 4715) //Disable "not all control paths return a value" diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index d52f7cf74..a79c5fe2e 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -73,6 +73,38 @@ std::map PetComponent::petFlags = { { 13067, 838 }, // Skeleton dragon }; +PetComponent::PetComponent(Entity* parentEntity, uint32_t componentId) : Component{ parentEntity }, + m_PetInfo{ CDClientManager::Instance().GetTable()->GetByID(componentId) } { + m_ComponentId = componentId; + m_Interaction = LWOOBJID_EMPTY; + m_InteractType = PetInteractType::none; + m_Owner = LWOOBJID_EMPTY; + m_ModerationStatus = 0; + m_Tamer = LWOOBJID_EMPTY; + m_ModelId = LWOOBJID_EMPTY; + m_Timer = 0; + m_TimerAway = 0; + m_TimerBounce = 0; + m_DatabaseId = LWOOBJID_EMPTY; + m_Flags = PetFlag::SPAWNING; // Tameable + m_Ability = ePetAbilityType::Invalid; + m_StartPosition = m_Parent->GetPosition(); + m_MovementAI = nullptr; + m_Preconditions = nullptr; + + m_ReadyToInteract = false; + SetPetAiState(PetAiState::spawn); + SetIsHandlingInteraction(false); + + std::string checkPreconditions = GeneralUtils::UTF16ToWTF8(parentEntity->GetVar(u"CheckPrecondition")); + + if (!checkPreconditions.empty()) { + SetPreconditions(checkPreconditions); + } + + m_FollowRadius = 8.0f; //Game::zoneManager->GetPetFollowRadius(); // TODO: FIX THIS TO LOAD DYNAMICALLY +} + void PetComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) { const bool tamed = m_Owner != LWOOBJID_EMPTY; diff --git a/dGame/dComponents/PetComponent.h b/dGame/dComponents/PetComponent.h index fb9d7f114..18f2a85c9 100644 --- a/dGame/dComponents/PetComponent.h +++ b/dGame/dComponents/PetComponent.h @@ -68,48 +68,13 @@ class PetComponent : public Component { public: inline static const eReplicaComponentType ComponentType = eReplicaComponentType::PET; - /** - * Pet information loaded from the CDClientDatabase - */ - CDPetComponent& m_PetInfo; - /** * PetComponent constructor * @param parentEntity The parent entity * @param componentId The component id */ - explicit PetComponent(Entity* parentEntity, uint32_t componentId) : Component{ parentEntity }, - m_PetInfo{ CDClientManager::Instance().GetTable()->GetByID(componentId) } { - m_ComponentId = componentId; - m_Interaction = LWOOBJID_EMPTY; - m_InteractType = PetInteractType::none; - m_Owner = LWOOBJID_EMPTY; - m_ModerationStatus = 0; - m_Tamer = LWOOBJID_EMPTY; - m_ModelId = LWOOBJID_EMPTY; - m_Timer = 0; - m_TimerAway = 0; - m_TimerBounce = 0; - m_DatabaseId = LWOOBJID_EMPTY; - m_Flags = PetFlag::SPAWNING; // Tameable - m_Ability = ePetAbilityType::Invalid; - m_StartPosition = m_Parent->GetPosition(); - m_MovementAI = nullptr; - m_Preconditions = nullptr; - - m_ReadyToInteract = false; - SetPetAiState(PetAiState::spawn); - SetIsHandlingInteraction(false); - - std::string checkPreconditions = GeneralUtils::UTF16ToWTF8(parentEntity->GetVar(u"CheckPrecondition")); - - if (!checkPreconditions.empty()) { - SetPreconditions(checkPreconditions); - } - - m_FollowRadius = 8.0f; //Game::zoneManager->GetPetFollowRadius(); // TODO: FIX THIS TO LOAD DYNAMICALLY - } - + explicit PetComponent(Entity* parentEntity, uint32_t componentId); + ~PetComponent() override; /** @@ -615,6 +580,11 @@ class PetComponent : public Component { * Preconditions that need to be met before an entity can tame this pet */ PreconditionExpression* m_Preconditions; + + /** + * Pet information loaded from the CDClientDatabase + */ + CDPetComponent& m_PetInfo; }; #endif // PETCOMPONENT_H From 70e0cd6f93dfe1ee4996039171acbf610ae46982 Mon Sep 17 00:00:00 2001 From: jadebenn Date: Tue, 13 Feb 2024 20:59:35 -0600 Subject: [PATCH 52/73] more merge --- dGame/dComponents/PetComponent.cpp | 13 +++-- dGame/dUtilities/SlashCommandHandler.cpp | 69 ++++++++++-------------- 2 files changed, 34 insertions(+), 48 deletions(-) diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index 111aa8953..48634f4ba 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -767,7 +767,7 @@ void PetComponent::Wander() { void PetComponent::OnSpawn() { m_MovementAI = m_Parent->GetComponent(); - if (m_StartPosition == NiPoint3::ZERO) { + if (m_StartPosition == NiPoint3Constant::ZERO) { m_StartPosition = m_Parent->GetPosition(); } @@ -982,7 +982,7 @@ void PetComponent::HandleInteractBouncer() { const auto bouncerId = bouncerComp->GetParentEntity()->GetObjectID(); bouncerComp->SetPetBouncerEnabled(true); - GameMessages::SendRequestClientBounce(bouncerId, this->GetOwnerId(), NiPoint3::ZERO, NiPoint3::ZERO, bouncerId, true, false, UNASSIGNED_SYSTEM_ADDRESS); //TODO: Check packet captures!! + GameMessages::SendRequestClientBounce(bouncerId, this->GetOwnerId(), NiPoint3Constant::ZERO, NiPoint3Constant::ZERO, bouncerId, true, false, UNASSIGNED_SYSTEM_ADDRESS); //TODO: Check packet captures!! bouncerComp->SetPetBouncerEnabled(false); RenderComponent::PlayAnimation(petSwitchEntity, u"up"); }); @@ -990,7 +990,7 @@ void PetComponent::HandleInteractBouncer() { RenderComponent::PlayAnimation(petSwitchEntity, u"launch"); //u"engaged"); //TODO: Check if the timing on this is right // TODO: Need to freeze player movement until the bounce begins! - Command(NiPoint3::ZERO, LWOOBJID_EMPTY, 1, PetEmote::ActivateSwitch, true); // Plays 'jump on switch' animation + Command(NiPoint3Constant::ZERO, LWOOBJID_EMPTY, 1, PetEmote::ActivateSwitch, true); // Plays 'jump on switch' animation StopInteract(); } m_Timer += 0.5f; @@ -1043,7 +1043,7 @@ void PetComponent::StartInteractTreasureDig() { LOG_DEBUG("StartInteractTreasureDig() m_Flags = %d", m_Flags); Game::entityManager->SerializeEntity(m_Parent); - Command(NiPoint3::ZERO, LWOOBJID_EMPTY, 1, PetEmote::DigTreasure, true); // Plays 'dig' animation + Command(NiPoint3Constant::ZERO, LWOOBJID_EMPTY, 1, PetEmote::DigTreasure, true); // Plays 'dig' animation m_Timer = 2.0f; } @@ -1064,7 +1064,7 @@ void PetComponent::HandleInteractTreasureDig() { } if (m_TimerBounce <= 0.0f) { - Command(NiPoint3::ZERO, LWOOBJID_EMPTY, 1, PetEmote::Bounce, true); // Plays 'bounce' animation + Command(NiPoint3Constant::ZERO, LWOOBJID_EMPTY, 1, PetEmote::Bounce, true); // Plays 'bounce' animation m_TimerBounce = 1.0f; } @@ -1153,7 +1153,6 @@ void PetComponent::AddDrainImaginationTimer(Item* item, bool fromTaming) { if (!fromTaming) playerDestroyableComponent->Imagine(-1); // Set this to a variable so when this is called back from the player the timer doesn't fire off. - m_Parent->AddCallbackTimer(m_PetInfo.imaginationDrainRate, [playerDestroyableComponent, this, item]() { m_Parent->AddCallbackTimer(m_PetInfo.imaginationDrainRate, [playerDestroyableComponent, this, item]() { if (!playerDestroyableComponent) { LOG("No petComponent and/or no playerDestroyableComponent"); @@ -1230,7 +1229,7 @@ void PetComponent::Command(const NiPoint3& position, const LWOOBJID source, cons } // Add movement functionality - if (position != NiPoint3::ZERO) { + if (position != NiPoint3Constant::ZERO) { m_MovementAI->SetDestination(position); } } diff --git a/dGame/dUtilities/SlashCommandHandler.cpp b/dGame/dUtilities/SlashCommandHandler.cpp index d96a330ec..b989a4f1c 100644 --- a/dGame/dUtilities/SlashCommandHandler.cpp +++ b/dGame/dUtilities/SlashCommandHandler.cpp @@ -716,8 +716,8 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit return; } - uint32_t petStatus; - if (!GeneralUtils::TryParse(args[0], petStatus)) { + const auto petStatus = GeneralUtils::TryParse(args[0]); + if (!petStatus) { ChatPackets::SendSystemMessage(sysAddr, u"Invalid pet status!"); return; } @@ -729,10 +729,10 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit return; } - petComponent->SetStatus(petStatus); + petComponent->SetStatus(petStatus.value()); //Game::entityManager->SerializeEntity(petComponent->GetParentEntity()); - std::u16string msg = u"Set pet status to " + (GeneralUtils::to_u16string(petStatus)); + std::u16string msg = u"Set pet status to " + (GeneralUtils::to_u16string(petStatus.value())); ChatPackets::SendSystemMessage(sysAddr, msg); } @@ -743,14 +743,14 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit return; } - int32_t commandType; - if (!GeneralUtils::TryParse(args[0+3], commandType)) { + const auto commandType = GeneralUtils::TryParse(args[0+3]); + if (!commandType) { ChatPackets::SendSystemMessage(sysAddr, u"Invalid command type!"); return; } - int32_t typeId; - if (!GeneralUtils::TryParse(args[1+3], typeId)) { + const auto typeId = GeneralUtils::TryParse(args[1+3]); + if (!typeId) { ChatPackets::SendSystemMessage(sysAddr, u"Invalid command type id!"); return; } @@ -763,29 +763,16 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit } //Determine the positional coordinates - NiPoint3 commandPos{}; - float x, y, z; - if (!GeneralUtils::TryParse(args[0], x)) { - ChatPackets::SendSystemMessage(sysAddr, u"Invalid x."); - return; - } - - if (!GeneralUtils::TryParse(args[1], y)) { - ChatPackets::SendSystemMessage(sysAddr, u"Invalid y."); - return; - } - - if (!GeneralUtils::TryParse(args[2], z)) { - ChatPackets::SendSystemMessage(sysAddr, u"Invalid z."); + //NiPoint3 commandPos{}; + //float x, y, z; + const auto commandPos = GeneralUtils::TryParse(args[0], args[1], args[2]); + if (!commandPos) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid position."); return; } // Set command position - commandPos.SetX(x); - commandPos.SetY(y); - commandPos.SetZ(z); - - petComponent->Command(commandPos, entity->GetObjectID(), commandType, typeId, true); + petComponent->Command(commandPos.value(), entity->GetObjectID(), commandType.value(), typeId.value(), true); } // Pet speed utility @@ -795,8 +782,8 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit return; } - float petMaxSpeed; - if (!GeneralUtils::TryParse(args[0], petMaxSpeed)) { + const auto petMaxSpeed = GeneralUtils::TryParse(args[0]); + if (!petMaxSpeed) { ChatPackets::SendSystemMessage(sysAddr, u"Invalid speed!"); return; } @@ -811,9 +798,9 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit auto* movementAIComponent = petComponent->GetParentEntity()->GetComponent(); if (!movementAIComponent) return; - movementAIComponent->SetMaxSpeed(petMaxSpeed); + movementAIComponent->SetMaxSpeed(petMaxSpeed.value()); - std::u16string msg = u"Set pet max speed to " + (GeneralUtils::to_u16string(petMaxSpeed)); + std::u16string msg = u"Set pet max speed to " + (GeneralUtils::to_u16string(petMaxSpeed.value())); ChatPackets::SendSystemMessage(sysAddr, msg); } @@ -824,25 +811,25 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit return; } - float petAccel; - if (!GeneralUtils::TryParse(args[0], petAccel)) { + const auto petAccel = GeneralUtils::TryParse(args[0]); + if (!petAccel) { ChatPackets::SendSystemMessage(sysAddr, u"Invalid acceleration!"); return; } // Determine if player has a pet summoned - auto* petComponent = PetComponent::GetActivePet(entity->GetObjectID()); + auto* const petComponent = PetComponent::GetActivePet(entity->GetObjectID()); if (!petComponent) { ChatPackets::SendSystemMessage(sysAddr, u"No active pet found!"); return; } - auto* movementAIComponent = petComponent->GetParentEntity()->GetComponent(); + auto* const movementAIComponent = petComponent->GetParentEntity()->GetComponent(); if (!movementAIComponent) return; - movementAIComponent->SetAcceleration(petAccel); + movementAIComponent->SetAcceleration(petAccel.value()); - std::u16string msg = u"Set pet acceleration to " + (GeneralUtils::to_u16string(petAccel)); + std::u16string msg = u"Set pet acceleration to " + GeneralUtils::to_u16string(petAccel.value()); ChatPackets::SendSystemMessage(sysAddr, msg); } @@ -853,8 +840,8 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit return; } - float petHaltDistance; - if (!GeneralUtils::TryParse(args[0], petHaltDistance)) { + const auto petHaltDistance = GeneralUtils::TryParse(args[0]); + if (!petHaltDistance) { ChatPackets::SendSystemMessage(sysAddr, u"Invalid halt distance!"); return; } @@ -869,9 +856,9 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit auto* movementAIComponent = petComponent->GetParentEntity()->GetComponent(); if (!movementAIComponent) return; - movementAIComponent->SetHaltDistance(petHaltDistance); + movementAIComponent->SetHaltDistance(petHaltDistance.value()); - std::u16string msg = u"Set pet halt distance to " + (GeneralUtils::to_u16string(petHaltDistance)); + std::u16string msg = u"Set pet halt distance to " + GeneralUtils::to_u16string(petHaltDistance.value()); ChatPackets::SendSystemMessage(sysAddr, msg); } From a449e5323b6e5869129fcbf95329baf80f708d55 Mon Sep 17 00:00:00 2001 From: jadebenn Date: Sun, 3 Mar 2024 19:02:11 -0600 Subject: [PATCH 53/73] delete unused functions and update documentation --- dGame/dComponents/PetComponent.cpp | 129 ++++++++++------------- dGame/dComponents/PetComponent.h | 6 +- dGame/dUtilities/SlashCommandHandler.cpp | 12 +-- dScripts/02_server/Pets/DamagingPets.cpp | 8 +- docs/Commands.md | 4 + 5 files changed, 72 insertions(+), 87 deletions(-) diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index 48634f4ba..db7bbc17f 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -175,20 +175,20 @@ void PetComponent::OnUse(Entity* originator) { if (m_Owner != LWOOBJID_EMPTY) return; if (m_Tamer != LWOOBJID_EMPTY) { - auto* tamer = Game::entityManager->GetEntity(m_Tamer); + const auto* const tamer = Game::entityManager->GetEntity(m_Tamer); if (tamer != nullptr) return; m_Tamer = LWOOBJID_EMPTY; } - auto* inventoryComponent = originator->GetComponent(); + auto* const inventoryComponent = originator->GetComponent(); - if (inventoryComponent == nullptr) return; + if (!inventoryComponent) return; if (m_Preconditions != nullptr && !m_Preconditions->Check(originator, true)) return; - auto* movementAIComponent = m_Parent->GetComponent(); + auto* const movementAIComponent = m_Parent->GetComponent(); if (movementAIComponent != nullptr) { movementAIComponent->Stop(); @@ -240,11 +240,11 @@ void PetComponent::OnUse(Entity* originator) { imaginationCost = cached->second.imaginationCost; } - auto* destroyableComponent = originator->GetComponent(); + const auto* const destroyableComponent = originator->GetComponent(); - if (destroyableComponent == nullptr) return; + if (!destroyableComponent) return; - auto imagination = destroyableComponent->GetImagination(); + const auto imagination = destroyableComponent->GetImagination(); if (imagination < imaginationCost) return; @@ -257,9 +257,9 @@ void PetComponent::OnUse(Entity* originator) { return; } - auto petPosition = m_Parent->GetPosition(); + const auto petPosition = m_Parent->GetPosition(); - auto originatorPosition = originator->GetPosition(); + const auto originatorPosition = originator->GetPosition(); m_Parent->SetRotation(NiQuaternion::LookAt(petPosition, originatorPosition)); @@ -295,7 +295,7 @@ void PetComponent::OnUse(Entity* originator) { } - auto rotation = NiQuaternion::LookAt(position, petPosition); + const auto rotation = NiQuaternion::LookAt(position, petPosition); GameMessages::SendNotifyPetTamingMinigame( originator->GetObjectID(), @@ -345,21 +345,13 @@ void PetComponent::Update(float deltaTime) { // Remove "left behind" pets if (m_Owner != LWOOBJID_EMPTY) { - Entity* owner = GetOwner(); + const Entity* const owner = GetOwner(); if (!owner) { m_Parent->Kill(); return; } } - /*START_BITMASK_SWITCH(m_Flags) { - case TAMEABLE: - break; - - default: - LOG_DEBUG("Triggered default case!"); - }*/ - if (HasFlag(SPAWNING)) OnSpawn(); // Handle pet AI states @@ -417,9 +409,9 @@ void PetComponent::UpdateUnowned(float deltaTime) { //TODO: CURRENTLY UNUSED void PetComponent::TryBuild(uint32_t numBricks, bool clientFailed) { if (m_Tamer == LWOOBJID_EMPTY) return; - auto* tamer = Game::entityManager->GetEntity(m_Tamer); + auto* const tamer = Game::entityManager->GetEntity(m_Tamer); - if (tamer == nullptr) { + if (!tamer) { m_Tamer = LWOOBJID_EMPTY; return; @@ -429,9 +421,9 @@ void PetComponent::TryBuild(uint32_t numBricks, bool clientFailed) { if (cached == buildCache.end()) return; - auto* destroyableComponent = tamer->GetComponent(); + auto* const destroyableComponent = tamer->GetComponent(); - if (destroyableComponent == nullptr) return; + if (!destroyableComponent) return; auto imagination = destroyableComponent->GetImagination(); @@ -457,9 +449,9 @@ void PetComponent::TryBuild(uint32_t numBricks, bool clientFailed) { void PetComponent::NotifyTamingBuildSuccess(NiPoint3 position) { if (m_Tamer == LWOOBJID_EMPTY) return; - auto* tamer = Game::entityManager->GetEntity(m_Tamer); + auto* const tamer = Game::entityManager->GetEntity(m_Tamer); - if (tamer == nullptr) { + if (!tamer) { m_Tamer = LWOOBJID_EMPTY; return; @@ -480,7 +472,7 @@ void PetComponent::NotifyTamingBuildSuccess(NiPoint3 position) { info.rot = NiQuaternionConstant::IDENTITY; info.spawnerID = tamer->GetObjectID(); - auto* modelEntity = Game::entityManager->CreateEntity(info); + auto* const modelEntity = Game::entityManager->CreateEntity(info); m_ModelId = modelEntity->GetObjectID(); @@ -490,7 +482,7 @@ void PetComponent::NotifyTamingBuildSuccess(NiPoint3 position) { GameMessages::SendPetResponse(m_Tamer, m_Parent->GetObjectID(), 0, 10, 0, tamer->GetSystemAddress()); - auto* inventoryComponent = tamer->GetComponent(); + auto* const inventoryComponent = tamer->GetComponent(); if (!inventoryComponent) return; LWOOBJID petSubKey = ObjectIDManager::GenerateRandomObjectID(); @@ -511,7 +503,7 @@ void PetComponent::NotifyTamingBuildSuccess(NiPoint3 position) { inventoryComponent->AddItem(m_Parent->GetLOT(), 1, eLootSourceType::ACTIVITY, eInventoryType::MODELS, {}, LWOOBJID_EMPTY, true, false, petSubKey); - auto* item = inventoryComponent->FindItemBySubKey(petSubKey, MODELS); + auto* const item = inventoryComponent->FindItemBySubKey(petSubKey, MODELS); if (!item) return; DatabasePet databasePet{}; @@ -543,15 +535,15 @@ void PetComponent::NotifyTamingBuildSuccess(NiPoint3 position) { tamer->GetCharacter()->SetPlayerFlag(petFlags.at(m_Parent->GetLOT()), true); } - auto* missionComponent = tamer->GetComponent(); + auto* const missionComponent = tamer->GetComponent(); if (missionComponent != nullptr) { missionComponent->Progress(eMissionTaskType::PET_TAMING, m_Parent->GetLOT()); } - SetOnlyFlag(IDLE); // SetStatus(1); + SetOnlyFlag(IDLE); - auto* characterComponent = tamer->GetComponent(); + auto* const characterComponent = tamer->GetComponent(); if (characterComponent != nullptr) { characterComponent->UpdatePlayerStatistic(PetsTamed); } @@ -617,7 +609,7 @@ void PetComponent::RequestSetPetName(std::u16string name) { GameMessages::SendTerminateInteraction(m_Tamer, eTerminateType::FROM_INTERACTION, m_Parent->GetObjectID()); - auto* modelEntity = Game::entityManager->GetEntity(m_ModelId); + auto* const modelEntity = Game::entityManager->GetEntity(m_ModelId); if (modelEntity != nullptr) { modelEntity->Smash(m_Tamer); @@ -636,9 +628,9 @@ void PetComponent::RequestSetPetName(std::u16string name) { void PetComponent::ClientExitTamingMinigame(bool voluntaryExit) { if (m_Tamer == LWOOBJID_EMPTY) return; - auto* tamer = Game::entityManager->GetEntity(m_Tamer); + auto* const tamer = Game::entityManager->GetEntity(m_Tamer); - if (tamer == nullptr) { + if (!tamer) { m_Tamer = LWOOBJID_EMPTY; return; @@ -687,9 +679,9 @@ void PetComponent::StartTimer() { void PetComponent::ClientFailTamingMinigame() { if (m_Tamer == LWOOBJID_EMPTY) return; - auto* tamer = Game::entityManager->GetEntity(m_Tamer); + auto* const tamer = Game::entityManager->GetEntity(m_Tamer); - if (tamer == nullptr) { + if (!tamer) { m_Tamer = LWOOBJID_EMPTY; return; @@ -806,11 +798,11 @@ void PetComponent::OnFollow(const float deltaTime) { } // Determine if the "Lost Tags" mission has been completed and digging has been unlocked - auto* missionComponent = owner->GetComponent(); + const auto* const missionComponent = owner->GetComponent(); if (!missionComponent) return; const bool digUnlocked = missionComponent->GetMissionState(842) == eMissionState::COMPLETE; - Entity* closestTreasure = PetDigServer::GetClosestTresure(ownerPos); + const Entity* closestTreasure = PetDigServer::GetClosestTresure(ownerPos); const bool nonDragonForBone = closestTreasure->GetLOT() == 12192 && m_Parent->GetLOT() != 13067; if (!nonDragonForBone && closestTreasure != nullptr && digUnlocked) { const NiPoint3 treasurePos = closestTreasure->GetPosition(); @@ -893,7 +885,7 @@ void PetComponent::StartInteract(const NiPoint3& position, const PetInteractType } void PetComponent::StopInteract(bool bDontSerialize) { - Entity* owner = GetOwner(); + Entity* const owner = GetOwner(); if (!owner) return; const auto petAbility = ePetAbilityType::Invalid; @@ -917,7 +909,7 @@ void PetComponent::StopInteract(bool bDontSerialize) { } void PetComponent::SetupInteractBouncer() { - const auto* owner = GetOwner(); + const auto* const owner = GetOwner(); if (!owner) return; LOG_DEBUG("Setting up bouncer interaction!"); @@ -941,10 +933,10 @@ void PetComponent::SetupInteractBouncer() { } void PetComponent::StartInteractBouncer() { - Entity* user = GetOwner(); + Entity* const user = GetOwner(); if (IsHandlingInteraction() || !user) return; - auto* destroyableComponent = user->GetComponent(); + auto* const destroyableComponent = user->GetComponent(); if (!destroyableComponent) return; auto imagination = destroyableComponent->GetImagination(); @@ -1018,10 +1010,10 @@ void PetComponent::SetupInteractTreasureDig() { } void PetComponent::StartInteractTreasureDig() { - Entity* user = GetOwner(); + Entity* const user = GetOwner(); if (IsHandlingInteraction() || !user) return; - auto* destroyableComponent = user->GetComponent(); + auto* const destroyableComponent = user->GetComponent(); if (!destroyableComponent) return; auto imagination = destroyableComponent->GetImagination(); @@ -1077,7 +1069,7 @@ void PetComponent::Activate(Item* item, bool registerPet, bool fromTaming) { // m_ItemId = item->GetId(); m_DatabaseId = item->GetSubKey(); - auto* inventoryComponent = item->GetInventory()->GetComponent(); + auto* const inventoryComponent = item->GetInventory()->GetComponent(); if (inventoryComponent == nullptr) return; @@ -1085,10 +1077,10 @@ void PetComponent::Activate(Item* item, bool registerPet, bool fromTaming) { // m_Owner = inventoryComponent->GetParent()->GetObjectID(); - auto* owner = GetOwner(); + auto* const owner = GetOwner(); - if (owner == nullptr) return; - SetFlag(SPAWNING); //SetStatus(PetFlag::SPAWNING); + if (!owner) return; + SetFlag(SPAWNING); auto databaseData = inventoryComponent->GetDatabasePet(m_DatabaseId); @@ -1119,7 +1111,7 @@ void PetComponent::Activate(Item* item, bool registerPet, bool fromTaming) { // GameMessages::SendMarkInventoryItemAsActive(m_Owner, true, eUnequippableActiveType::PET, m_ItemId, GetOwner()->GetSystemAddress()); - activePets[m_Owner] = m_Parent->GetObjectID(); + activePets.emplace(m_Owner, m_Parent->GetObjectID()); Game::entityManager->SerializeEntity(m_Parent); @@ -1137,16 +1129,16 @@ void PetComponent::Activate(Item* item, bool registerPet, bool fromTaming) { // void PetComponent::AddDrainImaginationTimer(Item* item, bool fromTaming) { if (Game::config->GetValue("pets_take_imagination") != "1") return; - auto playerInventory = item->GetInventory(); + const auto* const playerInventory = item->GetInventory(); if (!playerInventory) return; - auto playerInventoryComponent = playerInventory->GetComponent(); + const auto* const playerInventoryComponent = playerInventory->GetComponent(); if (!playerInventoryComponent) return; - auto playerEntity = playerInventoryComponent->GetParent(); + const auto* const playerEntity = playerInventoryComponent->GetParent(); if (!playerEntity) return; - auto playerDestroyableComponent = playerEntity->GetComponent(); + auto* const playerDestroyableComponent = playerEntity->GetComponent(); if (!playerDestroyableComponent) return; // Drain by 1 when you summon pet or when this method is called, but not when we have just tamed this pet. @@ -1181,9 +1173,9 @@ void PetComponent::Deactivate() { m_Parent->Kill(); - auto* owner = GetOwner(); + const auto* const owner = GetOwner(); - if (owner == nullptr) return; + if (!owner) return; GameMessages::SendAddPetToPlayer(m_Owner, 0, u"", LWOOBJID_EMPTY, LOT_NULL, owner->GetSystemAddress()); @@ -1195,23 +1187,21 @@ void PetComponent::Deactivate() { } void PetComponent::Release() { - auto* inventoryComponent = GetOwner()->GetComponent(); + auto* const inventoryComponent = GetOwner()->GetComponent(); - if (inventoryComponent == nullptr) { - return; - } + if (!inventoryComponent) return; Deactivate(); inventoryComponent->RemoveDatabasePet(m_DatabaseId); - auto* item = inventoryComponent->FindItemBySubKey(m_DatabaseId); + auto* const item = inventoryComponent->FindItemBySubKey(m_DatabaseId); item->SetCount(0, false, false); } void PetComponent::Command(const NiPoint3& position, const LWOOBJID source, const int32_t commandType, const int32_t typeId, const bool overrideObey) { - auto* owner = GetOwner(); + auto* const owner = GetOwner(); if (!owner) return; if (commandType == 1) { @@ -1254,10 +1244,6 @@ LWOOBJID PetComponent::GetItemId() const { return m_ItemId; } -uint32_t PetComponent::GetStatus() const { - return m_Flags; -} - ePetAbilityType PetComponent::GetAbility() const { return m_Ability; } @@ -1266,11 +1252,6 @@ void PetComponent::SetInteraction(LWOOBJID value) { m_Interaction = value; } -void PetComponent::SetStatus(uint32_t value) { - m_Flags = value; - LOG_DEBUG("Pet status set to: %x", m_Flags); -} - void PetComponent::SetAbility(ePetAbilityType value) { m_Ability = value; } @@ -1282,9 +1263,9 @@ PetComponent* PetComponent::GetTamingPet(LWOOBJID tamer) { return nullptr; } - auto* entity = Game::entityManager->GetEntity(pair->second); + auto* const entity = Game::entityManager->GetEntity(pair->second); - if (entity == nullptr) { + if (!entity) { currentActivities.erase(tamer); return nullptr; @@ -1300,9 +1281,9 @@ PetComponent* PetComponent::GetActivePet(LWOOBJID owner) { return nullptr; } - auto* entity = Game::entityManager->GetEntity(pair->second); + auto* const entity = Game::entityManager->GetEntity(pair->second); - if (entity == nullptr) { + if (!entity) { activePets.erase(owner); return nullptr; diff --git a/dGame/dComponents/PetComponent.h b/dGame/dComponents/PetComponent.h index 001faa1e5..d8b08b736 100644 --- a/dGame/dComponents/PetComponent.h +++ b/dGame/dComponents/PetComponent.h @@ -38,6 +38,7 @@ enum PetInteractType : uint8_t { enum PetFlag : uint32_t { NONE, IDLE = 1 << 0, //0x01 - Seems to be "idle," which the game doesn't differentiate from "follow" + UNKNOWN2 = 1 << 1, //0x02, UNKNOWN4 = 1 << 2, //0x04 - FOLLOWING(?) BEING_TAMED = 1 << 4, //0x10, NOT_WAITING = 1 << 5, //0x20, @@ -297,13 +298,13 @@ class PetComponent final : public Component * bit map * @return the status of this pet */ - uint32_t GetStatus() const; + /*uint32_t GetStatus() const;*/ /** * Sets the current status of the pet * @param value the current status of the pet to set */ - void SetStatus(uint32_t value); + /*void SetStatus(uint32_t value);*/ /** * Returns an ability the pet may perform, currently unused @@ -585,7 +586,6 @@ class PetComponent final : public Component /** * Pet information loaded from the CDClientDatabase - * TODO: Switch to a reference when safe to do so */ CDPetComponent m_PetInfo; }; diff --git a/dGame/dUtilities/SlashCommandHandler.cpp b/dGame/dUtilities/SlashCommandHandler.cpp index b989a4f1c..f4f716755 100644 --- a/dGame/dUtilities/SlashCommandHandler.cpp +++ b/dGame/dUtilities/SlashCommandHandler.cpp @@ -710,17 +710,18 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit } // Pet status utility - if (chatCommand == "setpetstatus" && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER) { + if (chatCommand == "setpetflag" && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER) { if (args.size() == 0) { ChatPackets::SendSystemMessage(sysAddr, u"Too few arguments!"); return; } - const auto petStatus = GeneralUtils::TryParse(args[0]); - if (!petStatus) { + const auto petFlagInput = GeneralUtils::TryParse>(args[0]); + if (!petFlagInput) { ChatPackets::SendSystemMessage(sysAddr, u"Invalid pet status!"); return; } + const PetFlag petFlag = static_cast(1 << petFlagInput.value()); // Determine if player has a pet summoned auto* petComponent = PetComponent::GetActivePet(entity->GetObjectID()); @@ -729,10 +730,9 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit return; } - petComponent->SetStatus(petStatus.value()); - //Game::entityManager->SerializeEntity(petComponent->GetParentEntity()); + petComponent->SetFlag(petFlag); - std::u16string msg = u"Set pet status to " + (GeneralUtils::to_u16string(petStatus.value())); + std::u16string msg = u"Set pet flag to " + (GeneralUtils::to_u16string(petFlag)); ChatPackets::SendSystemMessage(sysAddr, msg); } diff --git a/dScripts/02_server/Pets/DamagingPets.cpp b/dScripts/02_server/Pets/DamagingPets.cpp index 6fd8c560e..2702fa782 100644 --- a/dScripts/02_server/Pets/DamagingPets.cpp +++ b/dScripts/02_server/Pets/DamagingPets.cpp @@ -74,13 +74,13 @@ void DamagingPets::OnTimerDone(Entity* self, std::string message) { } void DamagingPets::MakeUntamable(Entity* self) { - auto* petComponent = self->GetComponent(); + auto* const petComponent = self->GetComponent(); // If the pet is currently not being tamed, make it hostile - if (petComponent != nullptr && petComponent->GetStatus() != 5) { + if (petComponent != nullptr && !petComponent->HasFlag(PetFlag::NOT_WAITING)) { self->SetNetworkVar(u"bIAmTamable", false); self->SetVar(u"IsEvil", true); - petComponent->SetStatus(1); + petComponent->SetFlag(PetFlag::IDLE); auto* combatAIComponent = self->GetComponent(); if (combatAIComponent != nullptr) { @@ -110,7 +110,7 @@ void DamagingPets::ClearEffects(Entity* self) { auto* petComponent = self->GetComponent(); if (petComponent != nullptr) { - petComponent->SetStatus(67108866); + petComponent->SetFlag(PetFlag::TAMEABLE, PetFlag::UNKNOWN2); } auto* combatAIComponent = self->GetComponent(); diff --git a/docs/Commands.md b/docs/Commands.md index d485847f3..265ab60dd 100644 --- a/docs/Commands.md +++ b/docs/Commands.md @@ -97,6 +97,10 @@ These commands are primarily for development and testing. The usage of many of t |setcontrolscheme|`/setcontrolscheme `|Sets the character control scheme to the specified number.|8| |setcurrency|`/setcurrency `|Sets your coins.|8| |setflag|`/setflag (value) `|Sets the given inventory or health flag to the given value, where value can be one of "on" or "off". If no value is given, by default this adds the flag to your character (equivalent of calling `/setflag on `).|8| +|setpetaccel|`/setpetaccel (value) `|Sets the acceleration of the owner's pet to the given value.|8| +|setpetflag|`/setpetflag (value) `|Sets the pet flag to the given value, where the value is one of the bitshift values specified in the PetFlags enum.|8| +|setpetmaxspeed|`/setpetmaxspeed (value) `|Sets the maximum speed of the owner's pet to the given value.|8| +|setpethalt|`/setpethalt (value) `|Sets the halting distance of the owner's pet to the given value.|8| |setinventorysize|`/setinventorysize (inventory)` or
`/setinvsize (inventory)`|Sets your inventory size to the given size. If `inventory` is provided, the number or string will be used to set that inventory to the requested size. Alias: `/setinvsize`|8| |setuistate|`/setuistate `|Changes UI state.|8| |spawn|`/spawn `|Spawns an object at your location by id.|8| From c36bc8389ad991e144b750bf383357b8ac4e3571 Mon Sep 17 00:00:00 2001 From: jadebenn Date: Sun, 3 Mar 2024 19:50:13 -0600 Subject: [PATCH 54/73] fix pet taming minigame countdown --- dGame/dComponents/PetComponent.cpp | 30 +++++------------------------- dGame/dComponents/PetComponent.h | 6 ------ 2 files changed, 5 insertions(+), 31 deletions(-) diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index 392b1c112..556583891 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -343,13 +343,15 @@ void PetComponent::Update(float deltaTime) { return; } - // Remove "left behind" pets + // Remove "left behind" pets and handle failing pet taming minigame if (m_Owner != LWOOBJID_EMPTY) { const Entity* const owner = GetOwner(); if (!owner) { m_Parent->Kill(); return; } + } else { + ClientFailTamingMinigame(); // TODO: This is not despawning the built model correctly } if (HasFlag(SPAWNING)) OnSpawn(); @@ -384,28 +386,6 @@ void PetComponent::Update(float deltaTime) { } } -void PetComponent::UpdateUnowned(float deltaTime) { //TODO: CURRENTLY UNUSED - if (m_Tamer != LWOOBJID_EMPTY) { - if (m_Timer > 0) { - m_Timer -= deltaTime; - - if (m_Timer <= 0) { - m_Timer = 0; - - ClientFailTamingMinigame(); - } - } - } else { - if (m_Timer > 0) { - m_Timer -= deltaTime; - - if (m_Timer <= 0) Wander(); - } else { - m_Timer = 5; - } - } -} - void PetComponent::TryBuild(uint32_t numBricks, bool clientFailed) { if (m_Tamer == LWOOBJID_EMPTY) return; @@ -894,7 +874,7 @@ void PetComponent::StopInteract(bool bDontSerialize) { SetInteractType(PetInteractType::none); SetAbility(petAbility); SetPetAiState(PetAiState::follow); - SetOnlyFlag(IDLE); //SetStatus(PetFlag::NONE); + SetOnlyFlag(IDLE); SetIsReadyToInteract(false); SetIsHandlingInteraction(false); // Needed? m_MovementAI->SetMaxSpeed(m_PetInfo.sprintSpeed); @@ -1071,7 +1051,7 @@ void PetComponent::Activate(Item* item, bool registerPet, bool fromTaming) { // auto* const inventoryComponent = item->GetInventory()->GetComponent(); - if (inventoryComponent == nullptr) return; + if (!inventoryComponent) return; inventoryComponent->DespawnPet(); diff --git a/dGame/dComponents/PetComponent.h b/dGame/dComponents/PetComponent.h index 7f3e79aa9..0d823b530 100644 --- a/dGame/dComponents/PetComponent.h +++ b/dGame/dComponents/PetComponent.h @@ -139,12 +139,6 @@ class PetComponent final : public Component */ void Update(float deltaTime) override; - /** - * Handles updates for unowned pets - * @param deltaTime time since last update - */ - void UpdateUnowned(float deltaTime); - /** * Handles an OnUse event from another entity, initializing the pet taming minigame if this pet is untamed. * @param originator the entity that triggered the event From 4791b31dc2f1acc43975a875100af0940d1ef694 Mon Sep 17 00:00:00 2001 From: jadebenn Date: Tue, 5 Mar 2024 23:37:42 -0600 Subject: [PATCH 55/73] use class enums --- dGame/dComponents/PetComponent.cpp | 40 ++++++------- dGame/dComponents/PetComponent.h | 56 +++++++++++++++---- dGame/dUtilities/SlashCommandHandler.cpp | 2 +- .../dComponentsTests/PetComponentTests.cpp | 4 ++ 4 files changed, 69 insertions(+), 33 deletions(-) diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index 556583891..d549ec01c 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -111,7 +111,7 @@ void PetComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpd outBitStream.Write1(); // Always serialize as dirty for now - outBitStream.Write(m_Flags); + outBitStream.Write(m_Flags); outBitStream.Write(tamed ? m_Ability : ePetAbilityType::Invalid); // Something with the overhead icon? const bool interacting = m_Interaction != LWOOBJID_EMPTY; @@ -324,7 +324,7 @@ void PetComponent::OnUse(Entity* originator) { GameMessages::SendNotifyPetTamingPuzzleSelected(originator->GetObjectID(), bricks, originator->GetSystemAddress()); m_Tamer = originator->GetObjectID(); - SetFlag(IDLE, UNKNOWN4); //SetStatus(5); + SetFlag(PetFlag::IDLE, PetFlag::UNKNOWN4); //SetStatus(5); currentActivities.insert_or_assign(m_Tamer, m_Parent->GetObjectID()); @@ -354,7 +354,7 @@ void PetComponent::Update(float deltaTime) { ClientFailTamingMinigame(); // TODO: This is not despawning the built model correctly } - if (HasFlag(SPAWNING)) OnSpawn(); + if (HasFlag(PetFlag::SPAWNING)) OnSpawn(); // Handle pet AI states switch (m_State) { @@ -521,7 +521,7 @@ void PetComponent::NotifyTamingBuildSuccess(NiPoint3 position) { missionComponent->Progress(eMissionTaskType::PET_TAMING, m_Parent->GetLOT()); } - SetOnlyFlag(IDLE); + SetOnlyFlag(PetFlag::IDLE); auto* const characterComponent = tamer->GetComponent(); if (characterComponent != nullptr) { @@ -634,7 +634,7 @@ void PetComponent::ClientExitTamingMinigame(bool voluntaryExit) { currentActivities.erase(m_Tamer); - SetOnlyFlag(TAMEABLE); //SetStatus(PetFlag::TAMEABLE); + SetOnlyFlag(PetFlag::TAMEABLE); //SetStatus(PetFlag::TAMEABLE); m_Tamer = LWOOBJID_EMPTY; m_Timer = 0; @@ -685,7 +685,7 @@ void PetComponent::ClientFailTamingMinigame() { currentActivities.erase(m_Tamer); - SetOnlyFlag(TAMEABLE); //SetStatus(PetFlag::TAMEABLE); + SetOnlyFlag(PetFlag::TAMEABLE); //SetStatus(PetFlag::TAMEABLE); m_Tamer = LWOOBJID_EMPTY; m_Timer = 0; @@ -750,12 +750,12 @@ void PetComponent::OnSpawn() { //SetOnlyFlag(IDLE); //SetStatus(PetFlag::NONE); SetPetAiState(PetAiState::follow); } else { - SetFlag(TAMEABLE); + SetFlag(PetFlag::TAMEABLE); SetPetAiState(PetAiState::idle); } - SetFlag(IDLE); - UnsetFlag(SPAWNING); + SetFlag(PetFlag::IDLE); + UnsetFlag(PetFlag::SPAWNING); Game::entityManager->SerializeEntity(m_Parent); } @@ -874,7 +874,7 @@ void PetComponent::StopInteract(bool bDontSerialize) { SetInteractType(PetInteractType::none); SetAbility(petAbility); SetPetAiState(PetAiState::follow); - SetOnlyFlag(IDLE); + SetOnlyFlag(PetFlag::IDLE); SetIsReadyToInteract(false); SetIsHandlingInteraction(false); // Needed? m_MovementAI->SetMaxSpeed(m_PetInfo.sprintSpeed); @@ -897,8 +897,8 @@ void PetComponent::SetupInteractBouncer() { const auto petAbility = ePetAbilityType::JumpOnObject; SetAbility(petAbility); - UnsetFlag(IDLE); - SetFlag(ON_SWITCH, NOT_WAITING); //SetStatus(PetFlag::NOT_WAITING); // TODO: Double-check this is the right flag being set + UnsetFlag(PetFlag::IDLE); + SetFlag(PetFlag::ON_SWITCH, PetFlag::NOT_WAITING); //SetStatus(PetFlag::NOT_WAITING); // TODO: Double-check this is the right flag being set LOG_DEBUG("m_Flags = %d", m_Flags); Game::entityManager->SerializeEntity(m_Parent); // TODO: Double-check pet packet captures @@ -962,7 +962,7 @@ void PetComponent::HandleInteractBouncer() { RenderComponent::PlayAnimation(petSwitchEntity, u"launch"); //u"engaged"); //TODO: Check if the timing on this is right // TODO: Need to freeze player movement until the bounce begins! - Command(NiPoint3Constant::ZERO, LWOOBJID_EMPTY, 1, PetEmote::ActivateSwitch, true); // Plays 'jump on switch' animation + Command(NiPoint3Constant::ZERO, LWOOBJID_EMPTY, 1, GeneralUtils::CastUnderlyingType(PetEmote::ActivateSwitch), true); // Plays 'jump on switch' animation StopInteract(); } m_Timer += 0.5f; @@ -977,8 +977,8 @@ void PetComponent::SetupInteractTreasureDig() { const auto petAbility = ePetAbilityType::DigAtPosition; SetAbility(petAbility); - UnsetFlag(IDLE); - SetFlag(ON_SWITCH, NOT_WAITING); //SetStatus(PetFlag::NOT_WAITING); // TODO: Double-check this is the right flag being set + UnsetFlag(PetFlag::IDLE); + SetFlag(PetFlag::ON_SWITCH, PetFlag::NOT_WAITING); //SetStatus(PetFlag::NOT_WAITING); // TODO: Double-check this is the right flag being set LOG_DEBUG("m_Flags = %d", m_Flags); Game::entityManager->SerializeEntity(m_Parent); // TODO: Double-check pet packet captures @@ -1010,12 +1010,12 @@ void PetComponent::StartInteractTreasureDig() { Game::entityManager->SerializeEntity(user); SetIsHandlingInteraction(true); - UnsetFlag(ON_SWITCH, NOT_WAITING); // TODO: FIND THE CORRECT STATUS TO USE HERE - SetFlag(IDLE); + UnsetFlag(PetFlag::ON_SWITCH, PetFlag::NOT_WAITING); // TODO: FIND THE CORRECT STATUS TO USE HERE + SetFlag(PetFlag::IDLE); LOG_DEBUG("StartInteractTreasureDig() m_Flags = %d", m_Flags); Game::entityManager->SerializeEntity(m_Parent); - Command(NiPoint3Constant::ZERO, LWOOBJID_EMPTY, 1, PetEmote::DigTreasure, true); // Plays 'dig' animation + Command(NiPoint3Constant::ZERO, LWOOBJID_EMPTY, 1, GeneralUtils::CastUnderlyingType(PetEmote::DigTreasure), true); // Plays 'dig' animation m_Timer = 2.0f; } @@ -1036,7 +1036,7 @@ void PetComponent::HandleInteractTreasureDig() { } if (m_TimerBounce <= 0.0f) { - Command(NiPoint3Constant::ZERO, LWOOBJID_EMPTY, 1, PetEmote::Bounce, true); // Plays 'bounce' animation + Command(NiPoint3Constant::ZERO, LWOOBJID_EMPTY, 1, GeneralUtils::CastUnderlyingType(PetEmote::Bounce), true); // Plays 'bounce' animation m_TimerBounce = 1.0f; } @@ -1060,7 +1060,7 @@ void PetComponent::Activate(Item* item, bool registerPet, bool fromTaming) { // auto* const owner = GetOwner(); if (!owner) return; - SetFlag(SPAWNING); + SetFlag(PetFlag::SPAWNING); auto databaseData = inventoryComponent->GetDatabasePet(m_DatabaseId); diff --git a/dGame/dComponents/PetComponent.h b/dGame/dComponents/PetComponent.h index 0d823b530..218c98bcf 100644 --- a/dGame/dComponents/PetComponent.h +++ b/dGame/dComponents/PetComponent.h @@ -26,7 +26,7 @@ enum class PetAiState : uint8_t { /* * The type of object the pet is interacting with */ -enum PetInteractType : uint8_t { +enum class PetInteractType : uint8_t { none, // Not interacting treasure, // Treasure dig bouncer // Bouncer switch @@ -35,7 +35,7 @@ enum PetInteractType : uint8_t { /** * The flags governing the status of the pet: Governs the icon above their head and the interactions available */ -enum PetFlag : uint32_t { +enum class PetFlag : uint32_t { NONE, IDLE = 1 << 0, //0x01 - Seems to be "idle," which the game doesn't differentiate from "follow" UNKNOWN2 = 1 << 1, //0x02, @@ -49,14 +49,36 @@ enum PetFlag : uint32_t { TAMEABLE = 1 << 26 //0x4000000 }; -/* -132 = 128 + 4 +/** + * Define bitwise operators for PetFlag (TODO: Encapsulate into proper class) */ +constexpr PetFlag operator|(const PetFlag lhs, const PetFlag rhs) { + using underlying_type = std::underlying_type_t; + return static_cast(static_cast(lhs) | static_cast(rhs)); +} + +constexpr PetFlag& operator|=(PetFlag& lhs, const PetFlag rhs) { + return lhs = lhs | rhs; +} + +constexpr PetFlag operator&(const PetFlag lhs, const PetFlag rhs) { + using underlying_type = std::underlying_type_t; + return static_cast(static_cast(lhs) & static_cast(rhs)); +} + +constexpr PetFlag& operator&=(PetFlag& lhs, const PetFlag rhs) { + return lhs = lhs & rhs; +} + +constexpr PetFlag operator~(const PetFlag flag) { + using underlying_type = std::underlying_type_t; + return static_cast(~static_cast(flag)); +} /** * The pet emote animation ids that can used in PetComponent::Command() */ -enum PetEmote : int32_t { +enum class PetEmote : int32_t { ActivateSwitch = 201, DigTreasure, Bounce @@ -96,42 +118,52 @@ class PetComponent final : public Component /** * Gets the AI state of the pet */ - PetAiState GetPetAiState() { return m_State; }; + PetAiState GetPetAiState() const { return m_State; } /** * Sets one or more pet flags * @param flag PetFlag(s) to set */ template - void SetFlag(varArg... flag) { m_Flags |= (static_cast(flag) | ...); }; + void SetFlag(varArg... flag) { + m_Flags |= (flag | ...); + } /** * Sets the pet to ONLY have the specified flag(s), clearing all others * @param flag PetFlag(s) to set exclusively */ template - void SetOnlyFlag(varArg... flag) { m_Flags = (static_cast(flag) | ...); }; + void SetOnlyFlag(varArg... flag) { + m_Flags = (flag | ...); + } /** * Unsets one or more pet flags * @param flag PetFlag(s) to unset */ template - void UnsetFlag(varArg... flag) { m_Flags &= ~(static_cast(flag) | ...); }; + void UnsetFlag(varArg... flag) { + m_Flags &= ~(flag | ...); + } /** * Returns true if the pet has all the specified flag(s) * @param flag PetFlag(s) to check */ template - const bool HasFlag(varArg... flag) { return (m_Flags & (static_cast(flag) | ...)) == (static_cast(flag) | ...); }; + bool HasFlag(varArg... flag) const { + return (m_Flags & (flag | ...)) == (flag | ...); + } /** * Returns true if the pet has ONLY the specified flag(s) * @param flag PetFlag(s) to check if the pet has exclusively */ template - const bool HasOnlyFlag(varArg... flag) { return m_Flags == (static_cast(flag) | ...); }; + bool HasOnlyFlag(varArg... flag) const { + return m_Flags == (flag | ...); + } /** * Governs the pet update loop @@ -526,7 +558,7 @@ class PetComponent final : public Component /** * The current flags of the pet (e.g. tamable, tamed, etc). */ - uint32_t m_Flags; + PetFlag m_Flags; /** * The current state of the pet AI diff --git a/dGame/dUtilities/SlashCommandHandler.cpp b/dGame/dUtilities/SlashCommandHandler.cpp index 23d1ba995..c89694e8d 100644 --- a/dGame/dUtilities/SlashCommandHandler.cpp +++ b/dGame/dUtilities/SlashCommandHandler.cpp @@ -732,7 +732,7 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit petComponent->SetFlag(petFlag); - std::u16string msg = u"Set pet flag to " + (GeneralUtils::to_u16string(petFlag)); + std::u16string msg = u"Set pet flag to " + (GeneralUtils::to_u16string(GeneralUtils::CastUnderlyingType(petFlag))); ChatPackets::SendSystemMessage(sysAddr, msg); } diff --git a/tests/dGameTests/dComponentsTests/PetComponentTests.cpp b/tests/dGameTests/dComponentsTests/PetComponentTests.cpp index 9e063f210..4ded62193 100644 --- a/tests/dGameTests/dComponentsTests/PetComponentTests.cpp +++ b/tests/dGameTests/dComponentsTests/PetComponentTests.cpp @@ -32,6 +32,8 @@ class PetTest : public GameDependenciesTest { }; TEST_F(PetTest, PlacementNewAddComponentTest) { + using enum PetFlag; + // Test adding component ASSERT_NE(petComponent, nullptr); baseEntity->AddComponent(1); @@ -48,6 +50,8 @@ TEST_F(PetTest, PlacementNewAddComponentTest) { * Test bitset pet flags */ TEST_F(PetTest, PetComponentFlagTest) { + using enum PetFlag; + // Test setting and reading single flags, exclusively petComponent->SetOnlyFlag(NONE); ASSERT_TRUE(petComponent->HasOnlyFlag(NONE)); From e832dfbf719452b8a0b8d536ff27cbb9fe8c147b Mon Sep 17 00:00:00 2001 From: jadebenn Date: Fri, 8 Mar 2024 18:43:40 -0600 Subject: [PATCH 56/73] add most pet messages --- dCommon/dEnums/eHelpType.h | 8 ++++++ dGame/dComponents/InventoryComponent.cpp | 2 +- dGame/dComponents/PetComponent.cpp | 31 +++++++++++++----------- dGame/dComponents/PetComponent.h | 4 +-- dGame/dGameMessages/GameMessages.cpp | 2 +- dGame/dInventory/Item.cpp | 2 +- dGame/dUtilities/SlashCommandHandler.cpp | 30 ++++++++++++++++++++++- docs/Commands.md | 1 + 8 files changed, 60 insertions(+), 20 deletions(-) diff --git a/dCommon/dEnums/eHelpType.h b/dCommon/dEnums/eHelpType.h index c2fa70cd0..6f6af1727 100644 --- a/dCommon/dEnums/eHelpType.h +++ b/dCommon/dEnums/eHelpType.h @@ -3,6 +3,8 @@ #include +#include "magic_enum.hpp" + enum class eHelpType : int32_t { NONE = 0, UNLOCK_MINIMAP = 2, @@ -37,4 +39,10 @@ enum class eHelpType : int32_t { UI_INVENTORY_FULL_CANNOT_PICKUP_ITEM = 86 }; +template <> +struct magic_enum::customize::enum_range { + static constexpr int min = 0; + static constexpr int max = 86; +}; + #endif //!__EHELPTYPE__H__ diff --git a/dGame/dComponents/InventoryComponent.cpp b/dGame/dComponents/InventoryComponent.cpp index 161d7b916..ecca0d289 100644 --- a/dGame/dComponents/InventoryComponent.cpp +++ b/dGame/dComponents/InventoryComponent.cpp @@ -1204,7 +1204,7 @@ void InventoryComponent::SpawnPet(Item* item) { auto* current = PetComponent::GetActivePet(m_Parent->GetObjectID()); if (current != nullptr) { - current->Deactivate(); + current->Deactivate(eHelpType::PET_DESPAWN_BY_OWNER_HIBERNATE); if (current->GetDatabaseId() == item->GetSubKey()) { return; diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index c42ba2ac6..fc277f0e0 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -324,7 +324,7 @@ void PetComponent::OnUse(Entity* originator) { GameMessages::SendNotifyPetTamingPuzzleSelected(originator->GetObjectID(), bricks, originator->GetSystemAddress()); m_Tamer = originator->GetObjectID(); - SetFlag(PetFlag::IDLE, PetFlag::UNKNOWN4); //SetStatus(5); + SetFlag(PetFlag::IDLE, PetFlag::UNKNOWN4); currentActivities.insert_or_assign(m_Tamer, m_Parent->GetObjectID()); @@ -424,7 +424,7 @@ void PetComponent::TryBuild(uint32_t numBricks, bool clientFailed) { GameMessages::SendPetTamingTryBuildResult(m_Tamer, !clientFailed, numBricks, tamer->GetSystemAddress()); } -void PetComponent::NotifyTamingBuildSuccess(NiPoint3 position) { +void PetComponent::NotifyTamingBuildSuccess(const NiPoint3 position) { if (m_Tamer == LWOOBJID_EMPTY) return; auto* const tamer = Game::entityManager->GetEntity(m_Tamer); @@ -527,7 +527,7 @@ void PetComponent::NotifyTamingBuildSuccess(NiPoint3 position) { } } -void PetComponent::RequestSetPetName(std::u16string name) { +void PetComponent::RequestSetPetName(const std::u16string& name) { if (m_Tamer == LWOOBJID_EMPTY) { if (m_Owner != LWOOBJID_EMPTY) { auto* owner = GetOwner(); @@ -630,7 +630,7 @@ void PetComponent::ClientExitTamingMinigame(bool voluntaryExit) { currentActivities.erase(m_Tamer); - SetOnlyFlag(PetFlag::TAMEABLE); //SetStatus(PetFlag::TAMEABLE); + SetOnlyFlag(PetFlag::TAMEABLE); m_Tamer = LWOOBJID_EMPTY; m_Timer = 0; @@ -679,7 +679,7 @@ void PetComponent::ClientFailTamingMinigame() { currentActivities.erase(m_Tamer); - SetOnlyFlag(PetFlag::TAMEABLE); //SetStatus(PetFlag::TAMEABLE); + SetOnlyFlag(PetFlag::TAMEABLE); m_Tamer = LWOOBJID_EMPTY; m_Timer = 0; @@ -917,6 +917,7 @@ void PetComponent::StartInteractBouncer() { //GameMessages::SendHelp(user->GetObjectID(), eHelpType::PR_NEED_IMAGINATION, user->GetSystemAddress()); // Check if right message! return; } + GameMessages::SendHelp(user->GetObjectID(), eHelpType::PR_TOOLTIP_1ST_PET_JUMPED_ON_SWITCH, user->GetSystemAddress()); GameMessages::SendShowPetActionButton(m_Owner, ePetAbilityType::Invalid, false, user->GetSystemAddress()); @@ -954,7 +955,7 @@ void PetComponent::HandleInteractBouncer() { RenderComponent::PlayAnimation(petSwitchEntity, u"launch"); //u"engaged"); //TODO: Check if the timing on this is right // TODO: Need to freeze player movement until the bounce begins! - Command(NiPoint3Constant::ZERO, LWOOBJID_EMPTY, 1, GeneralUtils::CastUnderlyingType(PetEmote::ActivateSwitch), true); // Plays 'jump on switch' animation + Command(NiPoint3Constant::ZERO, LWOOBJID_EMPTY, 1, GeneralUtils::ToUnderlying(PetEmote::ActivateSwitch), true); // Plays 'jump on switch' animation StopInteract(); } m_Timer += 0.5f; @@ -970,7 +971,7 @@ void PetComponent::SetupInteractTreasureDig() { SetAbility(petAbility); UnsetFlag(PetFlag::IDLE); - SetFlag(PetFlag::ON_SWITCH, PetFlag::NOT_WAITING); //SetStatus(PetFlag::NOT_WAITING); // TODO: Double-check this is the right flag being set + SetFlag(PetFlag::ON_SWITCH, PetFlag::NOT_WAITING); // TODO: Double-check this is the right flag being set LOG_DEBUG("m_Flags = %d", m_Flags); Game::entityManager->SerializeEntity(m_Parent); // TODO: Double-check pet packet captures @@ -1007,7 +1008,7 @@ void PetComponent::StartInteractTreasureDig() { LOG_DEBUG("StartInteractTreasureDig() m_Flags = %d", m_Flags); Game::entityManager->SerializeEntity(m_Parent); - Command(NiPoint3Constant::ZERO, LWOOBJID_EMPTY, 1, GeneralUtils::CastUnderlyingType(PetEmote::DigTreasure), true); // Plays 'dig' animation + Command(NiPoint3Constant::ZERO, LWOOBJID_EMPTY, 1, GeneralUtils::ToUnderlying(PetEmote::DigTreasure), true); // Plays 'dig' animation m_Timer = 2.0f; } @@ -1028,7 +1029,7 @@ void PetComponent::HandleInteractTreasureDig() { } if (m_TimerBounce <= 0.0f) { - Command(NiPoint3Constant::ZERO, LWOOBJID_EMPTY, 1, GeneralUtils::CastUnderlyingType(PetEmote::Bounce), true); // Plays 'bounce' animation + Command(NiPoint3Constant::ZERO, LWOOBJID_EMPTY, 1, GeneralUtils::ToUnderlying(PetEmote::Bounce), true); // Plays 'bounce' animation m_TimerBounce = 1.0f; } @@ -1124,19 +1125,21 @@ void PetComponent::AddDrainImaginationTimer(Item* item, bool fromTaming) { } // If we are out of imagination despawn the pet. - if (playerDestroyableComponent->GetImagination() == 0) { - this->Deactivate(); + if (playerDestroyableComponent->GetImagination() < 1) { + this->Deactivate(eHelpType::PR_NO_IMAGINATION_HIBERNATE); auto playerEntity = playerDestroyableComponent->GetParent(); if (!playerEntity) return; - - GameMessages::SendUseItemRequirementsResponse(playerEntity->GetObjectID(), playerEntity->GetSystemAddress(), eUseItemResponse::NoImaginationForPet); } this->AddDrainImaginationTimer(item); }); } -void PetComponent::Deactivate() { +void PetComponent::Deactivate(const eHelpType msg) { + if (msg != eHelpType::NONE) { + GameMessages::SendHelp(m_Parent->GetObjectID(), msg, m_Parent->GetSystemAddress()); + } + GameMessages::SendPlayFXEffect(m_Parent->GetObjectID(), -1, u"despawn", "", LWOOBJID_EMPTY, 1, 1, true); GameMessages::SendMarkInventoryItemAsActive(m_Owner, false, eUnequippableActiveType::PET, m_ItemId, GetOwner()->GetSystemAddress()); diff --git a/dGame/dComponents/PetComponent.h b/dGame/dComponents/PetComponent.h index 218c98bcf..568b86c01 100644 --- a/dGame/dComponents/PetComponent.h +++ b/dGame/dComponents/PetComponent.h @@ -196,7 +196,7 @@ class PetComponent final : public Component * successfully). * @param name the name of the pet to set */ - void RequestSetPetName(std::u16string name); + void RequestSetPetName(const std::u16string& name); /** * Handles a notification of the client that the taming entity is leaving the minigame, either voluntary or because @@ -266,7 +266,7 @@ class PetComponent final : public Component /** * Despawns the pet */ - void Deactivate(); + void Deactivate(eHelpType msg = eHelpType::NONE); /** * Removes the pet from the inventory diff --git a/dGame/dGameMessages/GameMessages.cpp b/dGame/dGameMessages/GameMessages.cpp index 5146d32ba..53a77777a 100644 --- a/dGame/dGameMessages/GameMessages.cpp +++ b/dGame/dGameMessages/GameMessages.cpp @@ -3823,7 +3823,7 @@ void GameMessages::HandleDespawnPet(RakNet::BitStream& inStream, Entity* entity, if (bDeletePet) { petComponent->Release(); } else { - petComponent->Deactivate(); + petComponent->Deactivate(eHelpType::PET_DESPAWN_BY_OWNER_HIBERNATE); } } diff --git a/dGame/dInventory/Item.cpp b/dGame/dInventory/Item.cpp index d3f15315a..d0acef4da 100644 --- a/dGame/dInventory/Item.cpp +++ b/dGame/dInventory/Item.cpp @@ -319,7 +319,7 @@ void Item::UseNonEquip(Item* item) { if (packageComponentId == 0) return; auto* packCompTable = CDClientManager::GetTable(); - auto packages = packCompTable->Query([=](const CDPackageComponent entry) {return entry.id == static_cast(packageComponentId); }); + auto packages = packCompTable->Query([packageComponentId](const CDPackageComponent entry) {return entry.id == static_cast(packageComponentId); }); auto success = !packages.empty(); if (success) { diff --git a/dGame/dUtilities/SlashCommandHandler.cpp b/dGame/dUtilities/SlashCommandHandler.cpp index c89694e8d..817df7972 100644 --- a/dGame/dUtilities/SlashCommandHandler.cpp +++ b/dGame/dUtilities/SlashCommandHandler.cpp @@ -59,6 +59,7 @@ #include "dpShapeSphere.h" #include "PossessableComponent.h" #include "PossessorComponent.h" +#include "StringifiedEnum.h" #include "HavokVehiclePhysicsComponent.h" #include "BuffComponent.h" #include "SkillComponent.h" @@ -81,6 +82,7 @@ #include "eControlScheme.h" #include "eConnectionType.h" #include "eChatInternalMessageType.h" +#include "eHelpType.h" #include "eMasterMessageType.h" #include "PlayerManager.h" @@ -674,6 +676,32 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit return; } + // Send packet to display help message pop-up + if (chatCommand == "helpmsg") { + if (entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER && args.size() == 1) { + const auto helpIdOpt = GeneralUtils::TryParse(args[0]); + if (!helpIdOpt) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid help message id."); + return; + } + + const eHelpType helpId = helpIdOpt.value(); + GameMessages::SendHelp(entity->GetObjectID(), helpId, sysAddr); + + // Convert and print enum string + const std::u16string msg = u"Sent help message '" + + GeneralUtils::ASCIIToUTF16(StringifiedEnum::ToString(helpId)) + + u"' (id: " + + GeneralUtils::to_u16string(GeneralUtils::ToUnderlying(helpId)) + + u')'; + + ChatPackets::SendSystemMessage(sysAddr, msg); + } else { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid command invocation."); + return; + } + } + if (chatCommand == "setflag" && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER && args.size() == 1) { const auto flagId = GeneralUtils::TryParse(args.at(0)); @@ -732,7 +760,7 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit petComponent->SetFlag(petFlag); - std::u16string msg = u"Set pet flag to " + (GeneralUtils::to_u16string(GeneralUtils::CastUnderlyingType(petFlag))); + std::u16string msg = u"Set pet flag to " + (GeneralUtils::to_u16string(GeneralUtils::ToUnderlying(petFlag))); ChatPackets::SendSystemMessage(sysAddr, msg); } diff --git a/docs/Commands.md b/docs/Commands.md index 265ab60dd..fb1252d8f 100644 --- a/docs/Commands.md +++ b/docs/Commands.md @@ -79,6 +79,7 @@ These commands are primarily for development and testing. The usage of many of t |getnavmeshheight|`/getnavmeshheight`|Displays the navmesh height at your current position.|8| |giveuscore|`/giveuscore `|Gives uscore.|8| |gmadditem|`/gmadditem (count)`|Adds the given item to your inventory by id.|8| +|helpmsg|`/helpmsg (help id)`|Display help message pop-up for a given id.|8| |inspect|`/inspect (-m \| -a \| -s \| -p \| -f (faction) \| -t)`|Finds the closest entity with the given component or LDF variable (ignoring players and racing cars), printing its ID, distance from the player, and whether it is sleeping, as well as the the IDs of all components the entity has. See [Detailed `/inspect` Usage](#detailed-inspect-usage) below.|8| |list-spawns|`/list-spawns`|Lists all the character spawn points in the zone. Additionally, this command will display the current scene that plays when the character lands in the next zone, if there is one.|8| |locrow|`/locrow`|Prints the your current position and rotation information to the console.|8| From 657fb355606396647d58d93a6ad7b0205319b87e Mon Sep 17 00:00:00 2001 From: jadebenn Date: Sun, 31 Mar 2024 21:49:59 -0500 Subject: [PATCH 57/73] semi update the unit tests --- dGame/dComponents/PetComponent.cpp | 9 +++-- .../dComponentsTests/PetComponentTests.cpp | 35 +++++++++++++++++++ 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index fc277f0e0..2bfe85356 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -153,11 +153,11 @@ void PetComponent::SetPetAiState(PetAiState newState) { } void PetComponent::OnUse(Entity* originator) { - LOG("PET USE!"); + LOG_DEBUG("PET USE!"); if (IsReadyToInteract()) { switch (GetAbility()) { - case ePetAbilityType::DigAtPosition: // Treasure dig TODO: FIX ICON + case ePetAbilityType::DigAtPosition: // Treasure dig StartInteractTreasureDig(); break; @@ -170,8 +170,7 @@ void PetComponent::OnUse(Entity* originator) { } } - // TODO: Rewrite everything below this comment - + // The minigame logic beneath this comment should be rewritten... eventually if (m_Owner != LWOOBJID_EMPTY) return; if (m_Tamer != LWOOBJID_EMPTY) { @@ -201,7 +200,7 @@ void PetComponent::OnUse(Entity* originator) { std::string buildFile; - // TODO: MOVE THIS OUT OF THE COMPONENT + // It may make sense to move this minigame-specific logic into another file if (cached == buildCache.end()) { auto query = CDClientDatabase::CreatePreppedStmt( "SELECT ValidPiecesLXF, PuzzleModelLot, Timelimit, NumValidPieces, imagCostPerBuild FROM TamingBuildPuzzles WHERE NPCLot = ?;"); diff --git a/tests/dGameTests/dComponentsTests/PetComponentTests.cpp b/tests/dGameTests/dComponentsTests/PetComponentTests.cpp index 4ded62193..05eeab752 100644 --- a/tests/dGameTests/dComponentsTests/PetComponentTests.cpp +++ b/tests/dGameTests/dComponentsTests/PetComponentTests.cpp @@ -99,3 +99,38 @@ TEST_F(PetTest, PetComponentFlagTest) { ASSERT_FALSE(petComponent->HasFlag(SPAWNING)); ASSERT_FALSE(petComponent->HasFlag(SPAWNING, NOT_WAITING, TAMEABLE, BEING_TAMED)); } + +TEST_F(PetTest, PetAiState) { + const auto initialState = petComponent->GetPetAiState(); + ASSERT_EQ(initialState, PetAiState::spawn); + + petComponent->SetPetAiState(PetAiState::follow); + ASSERT_EQ(PetAiState::follow, petComponent->GetPetAiState()); + + petComponent->SetPetAiState(PetAiState::idle); + ASSERT_EQ(PetAiState::idle, petComponent->GetPetAiState()); + + petComponent->SetPetAiState(PetAiState::interact); + ASSERT_EQ(PetAiState::interact, petComponent->GetPetAiState()); + + petComponent->SetPetAiState(PetAiState::despawn); + ASSERT_EQ(PetAiState::despawn, petComponent->GetPetAiState()); +} + +// Test the pet use logic +TEST_F(PetTest, PetUse) { + ASSERT_FALSE(petComponent->IsReadyToInteract()); + + petComponent->SetIsReadyToInteract(true); + ASSERT_TRUE(petComponent->IsReadyToInteract()); + + // Test bouncer logic + ASSERT_FALSE(petComponent->IsHandlingInteraction()); + petComponent->SetAbility(ePetAbilityType::JumpOnObject); + ASSERT_EQ(petComponent->GetAbility(), ePetAbilityType::JumpOnObject); + petComponent->SetInteractType(PetInteractType::bouncer); + petComponent->OnUse(baseEntity); + + // need to add a destroyable component to the test entity and test the imagination drain + +} From 36e0dbdb5e844b638a193f7c05d2cc91be31095e Mon Sep 17 00:00:00 2001 From: jadebenn Date: Thu, 18 Apr 2024 23:31:27 -0500 Subject: [PATCH 58/73] load imagination costs for pet abilities from CDClient --- dCommon/dEnums/ePetAbilityType.h | 6 +-- .../CDClientDatabase/CDClientManager.cpp | 17 +++----- .../CDClientTables/CDPetAbilitiesTable.cpp | 43 +++++++++++++++++++ .../CDClientTables/CDPetAbilitiesTable.h | 34 +++++++++++++++ .../CDClientTables/CDPetComponentTable.cpp | 10 ++--- .../CDClientTables/CDPetComponentTable.h | 4 +- .../CDClientTables/CMakeLists.txt | 1 + dGame/dComponents/PetComponent.cpp | 9 +++- 8 files changed, 102 insertions(+), 22 deletions(-) create mode 100644 dDatabase/CDClientDatabase/CDClientTables/CDPetAbilitiesTable.cpp create mode 100644 dDatabase/CDClientDatabase/CDClientTables/CDPetAbilitiesTable.h diff --git a/dCommon/dEnums/ePetAbilityType.h b/dCommon/dEnums/ePetAbilityType.h index 0cc6d6bd7..d5d8121d7 100644 --- a/dCommon/dEnums/ePetAbilityType.h +++ b/dCommon/dEnums/ePetAbilityType.h @@ -1,5 +1,5 @@ -#ifndef __EPETABILITYTYPE__H__ -#define __EPETABILITYTYPE__H__ +#ifndef EPETABILITYTYPE_H +#define EPETABILITYTYPE_H #include @@ -10,4 +10,4 @@ enum class ePetAbilityType : uint32_t { DigAtPosition }; -#endif //!__EPETABILITYTYPE__H__ +#endif //!EPETABILITYTYPE_H diff --git a/dDatabase/CDClientDatabase/CDClientManager.cpp b/dDatabase/CDClientDatabase/CDClientManager.cpp index 0e05c0b83..9a6021b6f 100644 --- a/dDatabase/CDClientDatabase/CDClientManager.cpp +++ b/dDatabase/CDClientDatabase/CDClientManager.cpp @@ -20,6 +20,7 @@ #include "CDMissionsTable.h" #include "CDObjectSkillsTable.h" #include "CDObjectsTable.h" +#include "CDPetAbilitiesTable.h" #include "CDPhysicsComponentTable.h" #include "CDRebuildComponentTable.h" #include "CDScriptComponentTable.h" @@ -41,8 +42,6 @@ #include "CDRewardCodesTable.h" #include "CDPetComponentTable.h" -#include - #ifndef CDCLIENT_CACHE_ALL // Uncomment this to cache the full cdclient database into memory. This will make the server load faster, but will use more memory. // A vanilla CDClient takes about 46MB of memory + the regular world data. @@ -55,13 +54,6 @@ #define CDCLIENT_DONT_CACHE_TABLE(x) #endif -class CDClientConnectionException : public std::exception { -public: - virtual const char* what() const throw() { - return "CDClientDatabase is not connected!"; - } -}; - // Using a macro to reduce repetitive code and issues from copy and paste. // As a note, ## in a macro is used to concatenate two tokens together. @@ -97,6 +89,7 @@ DEFINE_TABLE_STORAGE(CDObjectSkillsTable); DEFINE_TABLE_STORAGE(CDObjectsTable); DEFINE_TABLE_STORAGE(CDPhysicsComponentTable); DEFINE_TABLE_STORAGE(CDPackageComponentTable); +DEFINE_TABLE_STORAGE(CDPetAbilitiesTable); DEFINE_TABLE_STORAGE(CDPetComponentTable); DEFINE_TABLE_STORAGE(CDProximityMonitorComponentTable); DEFINE_TABLE_STORAGE(CDPropertyEntranceComponentTable); @@ -112,7 +105,9 @@ DEFINE_TABLE_STORAGE(CDVendorComponentTable); DEFINE_TABLE_STORAGE(CDZoneTableTable); void CDClientManager::LoadValuesFromDatabase() { - if (!CDClientDatabase::isConnected) throw CDClientConnectionException(); + if (!CDClientDatabase::isConnected) { + throw std::runtime_error{ "CDClientDatabase is not connected!" }; + } CDActivityRewardsTable::Instance().LoadValuesFromDatabase(); CDActivitiesTable::Instance().LoadValuesFromDatabase(); @@ -141,6 +136,7 @@ void CDClientManager::LoadValuesFromDatabase() { CDCLIENT_DONT_CACHE_TABLE(CDObjectsTable::Instance().LoadValuesFromDatabase()); CDPhysicsComponentTable::Instance().LoadValuesFromDatabase(); CDPackageComponentTable::Instance().LoadValuesFromDatabase(); + CDPetAbilitiesTable::Instance().LoadValuesFromDatabase(); CDPetComponentTable::Instance().LoadValuesFromDatabase(); CDProximityMonitorComponentTable::Instance().LoadValuesFromDatabase(); CDPropertyEntranceComponentTable::Instance().LoadValuesFromDatabase(); @@ -159,5 +155,6 @@ void CDClientManager::LoadValuesFromDatabase() { void CDClientManager::LoadValuesFromDefaults() { LOG("Loading default CDClient tables!"); + CDPetAbilitiesTable::Instance().LoadValuesFromDefaults(); CDPetComponentTable::Instance().LoadValuesFromDefaults(); } diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDPetAbilitiesTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDPetAbilitiesTable.cpp new file mode 100644 index 000000000..50f8a7171 --- /dev/null +++ b/dDatabase/CDClientDatabase/CDClientTables/CDPetAbilitiesTable.cpp @@ -0,0 +1,43 @@ +#include "CDPetAbilitiesTable.h" +#include "ePetAbilityType.h" + +namespace { + // Default entries for fallback + CDPetAbilities defaultEntry{ + .id = ePetAbilityType::Invalid, + UNUSED_ENTRY(.AbilityName = "invalid",) + .imaginationCost = 0, + UNUSED_ENTRY(.locStatus = 2,) + }; +} + +void CDPetAbilitiesTable::LoadValuesFromDatabase() { + auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM PetAbilities"); + auto& entries = GetEntriesMutable(); + while (!tableData.eof()) { + const auto abilityId = + static_cast(tableData.getIntField("id", static_cast(defaultEntry.id))); + auto& entry = entries[abilityId]; + + entry.id = abilityId; + UNUSED_COLUMN(entry.abilityName = tableData.getStringField("AbilityName", defaultEntry.abilityName)); + entry.imaginationCost = tableData.getIntField("ImaginationCost", defaultEntry.imaginationCost); + UNUSED_COLUMN(entry.locStatus = tableData.getIntField("locStatus", defaultEntry.locStatus)); + + tableData.nextRow(); + } +} + +void CDPetAbilitiesTable::LoadValuesFromDefaults() { + GetEntriesMutable().emplace(defaultEntry.id, defaultEntry); +} + +const CDPetAbilities& CDPetAbilitiesTable::GetByID(const ePetAbilityType id) { + const auto& entries = GetEntries(); + const auto itr = entries.find(id); + if (itr == entries.cend()) { + LOG("Unable to load pet ability (ID %i) values from database! Using default values instead.", id); + return defaultEntry; + } + return itr->second; +} diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDPetAbilitiesTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDPetAbilitiesTable.h new file mode 100644 index 000000000..6a0d9dc50 --- /dev/null +++ b/dDatabase/CDClientDatabase/CDClientTables/CDPetAbilitiesTable.h @@ -0,0 +1,34 @@ +#pragma once +#include "CDTable.h" +#include +#include + +// Forward declarations +enum class ePetAbilityType : uint32_t; + +struct CDPetAbilities { + ePetAbilityType id; + UNUSED_COLUMN(std::string abilityName;) + int32_t imaginationCost; + UNUSED_COLUMN(uint32_t locStatus;) +}; + +class CDPetAbilitiesTable : public CDTable> { +public: + + /** + * Load values from the CD client database + */ + void LoadValuesFromDatabase(); + + /** + * Load the default values into memory instead of attempting to connect to the CD client database + */ + void LoadValuesFromDefaults(); + + /** + * Gets the pet ability table corresponding to the pet ability ID + * @returns A pointer to the corresponding table, or nullptr if one could not be found + */ + const CDPetAbilities& GetByID(const ePetAbilityType id); +}; diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDPetComponentTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDPetComponentTable.cpp index f3371ecb8..078a5b101 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDPetComponentTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDPetComponentTable.cpp @@ -50,13 +50,13 @@ void CDPetComponentTable::LoadValuesFromDatabase() { } void CDPetComponentTable::LoadValuesFromDefaults() { - GetEntriesMutable().insert(std::make_pair(defaultEntry.id, defaultEntry)); + GetEntriesMutable().emplace(defaultEntry.id, defaultEntry); } -CDPetComponent& CDPetComponentTable::GetByID(const uint32_t componentID) { - auto& entries = GetEntriesMutable(); - auto itr = entries.find(componentID); - if (itr == entries.end()) { +const CDPetComponent& CDPetComponentTable::GetByID(const uint32_t componentID) { + const auto& entries = GetEntriesMutable(); + const auto itr = entries.find(componentID); + if (itr == entries.cend()) { LOG("Unable to load pet component (ID %i) values from database! Using default values instead.", componentID); return defaultEntry; } diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDPetComponentTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDPetComponentTable.h index 428902534..1af2137aa 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDPetComponentTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDPetComponentTable.h @@ -28,7 +28,7 @@ class CDPetComponentTable : public CDTableGetImagination(); - const int32_t imaginationCost = 2; // TODO: Get rid of this magic number - make static variable from lookup + const auto imaginationCost = + CDClientManager::GetTable()->GetByID(ePetAbilityType::JumpOnObject).imaginationCost; + if (imagination < imaginationCost) { //GameMessages::SendHelp(user->GetObjectID(), eHelpType::PR_NEED_IMAGINATION, user->GetSystemAddress()); // Check if right message! return; @@ -989,7 +992,9 @@ void PetComponent::StartInteractTreasureDig() { if (!destroyableComponent) return; auto imagination = destroyableComponent->GetImagination(); - const int32_t imaginationCost = 1; // TODO: Get rid of this magic number - make static variable from lookup + const auto imaginationCost = + CDClientManager::GetTable()->GetByID(ePetAbilityType::DigAtPosition).imaginationCost; + if (imagination < imaginationCost) { //GameMessages::SendHelp(user->GetObjectID(), eHelpType::PR_NEED_IMAGINATION, user->GetSystemAddress()); // Check if right message! return; From 0b2453241b5243c1a61d4e4d97d40d4086b620ea Mon Sep 17 00:00:00 2001 From: jadebenn Date: Fri, 19 Apr 2024 21:53:52 -0500 Subject: [PATCH 59/73] clean up PetComponent --- dCommon/CMakeLists.txt | 1 + dCommon/Flag.h | 82 ++++++ dGame/dComponents/PetComponent.cpp | 144 +++------- dGame/dComponents/PetComponent.h | 272 +++++++----------- dScripts/02_server/Pets/DamagingPets.cpp | 6 +- tests/dCommonTests/CMakeLists.txt | 1 + tests/dCommonTests/FlagTests.cpp | 67 +++++ .../dComponentsTests/PetComponentTests.cpp | 102 ++++--- 8 files changed, 348 insertions(+), 327 deletions(-) create mode 100644 dCommon/Flag.h create mode 100644 tests/dCommonTests/FlagTests.cpp diff --git a/dCommon/CMakeLists.txt b/dCommon/CMakeLists.txt index d020ff72f..7bb52a67d 100644 --- a/dCommon/CMakeLists.txt +++ b/dCommon/CMakeLists.txt @@ -4,6 +4,7 @@ set(DCOMMON_SOURCES "BinaryIO.cpp" "dConfig.cpp" "Diagnostics.cpp" + "Flag.h" "Logger.cpp" "Game.cpp" "GeneralUtils.cpp" diff --git a/dCommon/Flag.h b/dCommon/Flag.h new file mode 100644 index 000000000..a26cbc69b --- /dev/null +++ b/dCommon/Flag.h @@ -0,0 +1,82 @@ +#include "GeneralUtils.h" + +#include +// TODO: Test bounds checking + +template + requires std::is_enum_v +class Flag { +public: + using type = T; + using underlying_type = std::underlying_type_t; + static constexpr auto MAX_FLAG_VAL = std::bit_width(sizeof(underlying_type)); + + /** + * Sets one or more flags + * @param flag Flag(s) to set + */ + template ... varArg> + constexpr void Set(varArg... flag) noexcept { + m_Flags |= (ConvertFlag(flag) | ...); + } + + /** + * Sets ONLY have the specified flag(s), clearing all others + * @param flag Flag(s) to set exclusively + */ + template ... varArg> + constexpr void Reset(varArg... flag) { + m_Flags = (ConvertFlag(flag) | ...); + } + + /** + * Unsets one or more flags + * @param flag Flag(s) to unset + */ + template ... varArg> + constexpr void Unset(varArg... flag) { + m_Flags &= ~(ConvertFlag(flag) | ...); + } + + /** + * Returns true all the specified flag(s) are set + * @param flag Flag(s) to check + */ + template ... varArg> + constexpr bool Has(varArg... flag) const { + return (m_Flags & (ConvertFlag(flag) | ...)) == (ConvertFlag(flag) | ...); + } + + /** + * Returns true ONLY the specified flag(s) are set + * @param flag Flag(s) to check + */ + template ... varArg> + constexpr bool HasOnly(varArg... flag) const { + return m_Flags == (ConvertFlag(flag) | ...); + } + + /** + * Operator overload to allow for '=' assignment + */ + constexpr Flag& operator=(const T value) { + Reset(value); + return *this; + } + +private: + [[nodiscard]] + static constexpr underlying_type ConvertFlag(const T flag) { + auto flag_val = GeneralUtils::ToUnderlying(flag); + if (flag_val != 0) { + return 1 << flag_val; + } + // This should theoeretically be possible to do at compile time, but idk how + if (std::bit_width(flag_val) > MAX_FLAG_VAL) { + throw std::runtime_error{ "Enum value too large to be a flag!" }; + } + return flag_val; + } + + underlying_type m_Flags; +}; diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index 6010b4e67..bc64f3be0 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -37,7 +37,7 @@ #include "eMissionState.h" #include "dNavMesh.h" -std::unordered_map PetComponent::buildCache{}; +std::unordered_map PetComponent::buildCache{}; std::unordered_map PetComponent::currentActivities{}; std::unordered_map PetComponent::activePets{}; @@ -45,7 +45,7 @@ std::unordered_map PetComponent::activePets{}; * Maps all the pet lots to a flag indicating that the player has caught it. All basic pets have been guessed by ObjID * while the faction ones could be checked using their respective missions. */ -std::map PetComponent::petFlags = { +std::map PetComponent::petFlags{ { 3050, 801 }, // Elephant { 3054, 803 }, // Cat { 3195, 806 }, // Triceratops @@ -78,31 +78,17 @@ std::map PetComponent::petFlags = { PetComponent::PetComponent(Entity* parentEntity, uint32_t componentId) : Component{ parentEntity } { m_PetInfo = CDClientManager::GetTable()->GetByID(componentId); // TODO: Make reference when safe m_ComponentId = componentId; - m_Interaction = LWOOBJID_EMPTY; - m_InteractType = PetInteractType::none; - m_Owner = LWOOBJID_EMPTY; - m_ModerationStatus = 0; - m_Tamer = LWOOBJID_EMPTY; - m_ModelId = LWOOBJID_EMPTY; - m_Timer = 0; - m_TimerAway = 0; - m_TimerBounce = 0; - m_DatabaseId = LWOOBJID_EMPTY; m_Flags = PetFlag::SPAWNING; // Tameable - m_Ability = ePetAbilityType::Invalid; m_StartPosition = m_Parent->GetPosition(); m_MovementAI = nullptr; - m_Preconditions = nullptr; m_ReadyToInteract = false; - SetPetAiState(PetAiState::spawn); + m_State = PetAiState::spawn; SetIsHandlingInteraction(false); std::string checkPreconditions = GeneralUtils::UTF16ToWTF8(parentEntity->GetVar(u"CheckPrecondition")); - - if (!checkPreconditions.empty()) { - SetPreconditions(checkPreconditions); - } + m_Preconditions = + checkPreconditions.empty() ? std::nullopt : std::make_optional(PreconditionExpression(checkPreconditions)); m_FollowRadius = 8.0f; //Game::zoneManager->GetPetFollowRadius(); // TODO: FIX THIS TO LOAD DYNAMICALLY } @@ -113,13 +99,13 @@ void PetComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpd outBitStream.Write1(); // Always serialize as dirty for now outBitStream.Write(m_Flags); - outBitStream.Write(tamed ? m_Ability : ePetAbilityType::Invalid); // Something with the overhead icon? + outBitStream.Write(tamed ? m_Interaction.ability : ePetAbilityType::Invalid); // Something with the overhead icon? - const bool interacting = m_Interaction != LWOOBJID_EMPTY; + const bool interacting = m_Interaction.obj != LWOOBJID_EMPTY; outBitStream.Write(interacting); if (interacting) { - outBitStream.Write(m_Interaction); + outBitStream.Write(m_Interaction.obj); } outBitStream.Write(tamed); @@ -148,16 +134,11 @@ void PetComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpd } } -void PetComponent::SetPetAiState(PetAiState newState) { - if (newState == GetPetAiState()) return; - this->m_State = newState; -} - void PetComponent::OnUse(Entity* originator) { LOG_DEBUG("PET USE!"); if (IsReadyToInteract()) { - switch (GetAbility()) { + switch (m_Interaction.ability) { case ePetAbilityType::DigAtPosition: // Treasure dig StartInteractTreasureDig(); break; @@ -186,7 +167,7 @@ void PetComponent::OnUse(Entity* originator) { if (!inventoryComponent) return; - if (m_Preconditions != nullptr && !m_Preconditions->Check(originator, true)) return; + if (m_Preconditions.has_value() && !m_Preconditions->Check(originator, true)) return; auto* const movementAIComponent = m_Parent->GetComponent(); @@ -223,7 +204,7 @@ void PetComponent::OnUse(Entity* originator) { buildFile = std::string(result.getStringField("ValidPiecesLXF")); - PetPuzzleData data; + PuzzleData data; data.buildFile = buildFile; data.puzzleModelLot = result.getIntField("PuzzleModelLot"); data.timeLimit = result.getFloatField("Timelimit"); @@ -324,7 +305,7 @@ void PetComponent::OnUse(Entity* originator) { GameMessages::SendNotifyPetTamingPuzzleSelected(originator->GetObjectID(), bricks, originator->GetSystemAddress()); m_Tamer = originator->GetObjectID(); - SetFlag(PetFlag::IDLE, PetFlag::UNKNOWN4); + m_Flags.Set(PetFlag::IDLE, PetFlag::UNKNOWN4); currentActivities.insert_or_assign(m_Tamer, m_Parent->GetObjectID()); @@ -352,7 +333,7 @@ void PetComponent::Update(float deltaTime) { ClientFailTamingMinigame(); // TODO: This is not despawning the built model correctly } - if (HasFlag(PetFlag::SPAWNING)) OnSpawn(); + if (m_Flags.Has(PetFlag::SPAWNING)) OnSpawn(); // Handle pet AI states switch (m_State) { @@ -519,7 +500,7 @@ void PetComponent::NotifyTamingBuildSuccess(const NiPoint3 position) { missionComponent->Progress(eMissionTaskType::PET_TAMING, m_Parent->GetLOT()); } - SetOnlyFlag(PetFlag::IDLE); + m_Flags.Reset(PetFlag::IDLE); auto* const characterComponent = tamer->GetComponent(); if (characterComponent != nullptr) { @@ -630,7 +611,7 @@ void PetComponent::ClientExitTamingMinigame(bool voluntaryExit) { currentActivities.erase(m_Tamer); - SetOnlyFlag(PetFlag::TAMEABLE); + m_Flags.Reset(PetFlag::TAMEABLE); m_Tamer = LWOOBJID_EMPTY; m_Timer = 0; @@ -679,7 +660,7 @@ void PetComponent::ClientFailTamingMinigame() { currentActivities.erase(m_Tamer); - SetOnlyFlag(PetFlag::TAMEABLE); + m_Flags.Reset(PetFlag::TAMEABLE); m_Tamer = LWOOBJID_EMPTY; m_Timer = 0; @@ -742,12 +723,12 @@ void PetComponent::OnSpawn() { //SetOnlyFlag(IDLE); //SetStatus(PetFlag::NONE); SetPetAiState(PetAiState::follow); } else { - SetFlag(PetFlag::TAMEABLE); + m_Flags.Set(PetFlag::TAMEABLE); SetPetAiState(PetAiState::idle); } - SetFlag(PetFlag::IDLE); - UnsetFlag(PetFlag::SPAWNING); + m_Flags.Set(PetFlag::IDLE); + m_Flags.Unset(PetFlag::SPAWNING); Game::entityManager->SerializeEntity(m_Parent); } @@ -825,7 +806,7 @@ void PetComponent::OnInteract() { return; } - switch (GetInteractType()) { + switch (m_Interaction.type) { case PetInteractType::bouncer: if (IsReadyToInteract()) HandleInteractBouncer(); else SetupInteractBouncer(); @@ -844,10 +825,10 @@ void PetComponent::OnInteract() { } } -void PetComponent::StartInteract(const NiPoint3& position, const PetInteractType interactType, const LWOOBJID& interactID) { - SetInteraction(interactID); // TODO: Check if this should be serialized for goToObj - SetInteractType(interactType); - SetAbility(ePetAbilityType::GoToObject); +void PetComponent::StartInteract(const NiPoint3& position, const PetInteractType interactionType, const LWOOBJID& interactID) { + m_Interaction.obj; // TODO: Check if this should be serialized for goToObj + m_Interaction.type = interactionType; + m_Interaction.ability = ePetAbilityType::GoToObject; SetPetAiState(PetAiState::goToObj); m_MovementAI->SetMaxSpeed(m_PetInfo.runSpeed); m_MovementAI->SetHaltDistance(0.0f); @@ -860,13 +841,13 @@ void PetComponent::StopInteract(bool bDontSerialize) { Entity* const owner = GetOwner(); if (!owner) return; - const auto petAbility = ePetAbilityType::Invalid; + constexpr auto petAbility = ePetAbilityType::Invalid; - SetInteraction(LWOOBJID_EMPTY); - SetInteractType(PetInteractType::none); - SetAbility(petAbility); + m_Interaction.obj = LWOOBJID_EMPTY; + m_Interaction.type = PetInteractType::none; + m_Interaction.ability = petAbility; SetPetAiState(PetAiState::follow); - SetOnlyFlag(PetFlag::IDLE); + m_Flags.Reset(PetFlag::IDLE); SetIsReadyToInteract(false); SetIsHandlingInteraction(false); // Needed? m_MovementAI->SetMaxSpeed(m_PetInfo.sprintSpeed); @@ -886,11 +867,11 @@ void PetComponent::SetupInteractBouncer() { LOG_DEBUG("Setting up bouncer interaction!"); SetIsReadyToInteract(true); - const auto petAbility = ePetAbilityType::JumpOnObject; + constexpr auto petAbility = ePetAbilityType::JumpOnObject; - SetAbility(petAbility); - UnsetFlag(PetFlag::IDLE); - SetFlag(PetFlag::ON_SWITCH, PetFlag::NOT_WAITING); //SetStatus(PetFlag::NOT_WAITING); // TODO: Double-check this is the right flag being set + m_Interaction.ability = petAbility; + m_Flags.Unset(PetFlag::IDLE); + m_Flags.Set(PetFlag::ON_SWITCH, PetFlag::NOT_WAITING); //SetStatus(PetFlag::NOT_WAITING); // TODO: Double-check this is the right flag being set LOG_DEBUG("m_Flags = %d", m_Flags); Game::entityManager->SerializeEntity(m_Parent); // TODO: Double-check pet packet captures @@ -969,11 +950,11 @@ void PetComponent::SetupInteractTreasureDig() { LOG_DEBUG("Setting up dig interaction!"); SetIsReadyToInteract(true); - const auto petAbility = ePetAbilityType::DigAtPosition; + constexpr auto petAbility = ePetAbilityType::DigAtPosition; - SetAbility(petAbility); - UnsetFlag(PetFlag::IDLE); - SetFlag(PetFlag::ON_SWITCH, PetFlag::NOT_WAITING); // TODO: Double-check this is the right flag being set + m_Interaction.ability = petAbility; + m_Flags.Unset(PetFlag::IDLE); + m_Flags.Set(PetFlag::ON_SWITCH, PetFlag::NOT_WAITING); // TODO: Double-check this is the right flag being set LOG_DEBUG("m_Flags = %d", m_Flags); Game::entityManager->SerializeEntity(m_Parent); // TODO: Double-check pet packet captures @@ -1007,8 +988,8 @@ void PetComponent::StartInteractTreasureDig() { Game::entityManager->SerializeEntity(user); SetIsHandlingInteraction(true); - UnsetFlag(PetFlag::ON_SWITCH, PetFlag::NOT_WAITING); // TODO: FIND THE CORRECT STATUS TO USE HERE - SetFlag(PetFlag::IDLE); + m_Flags.Unset(PetFlag::ON_SWITCH, PetFlag::NOT_WAITING); // TODO: FIND THE CORRECT STATUS TO USE HERE + m_Flags.Set(PetFlag::IDLE); LOG_DEBUG("StartInteractTreasureDig() m_Flags = %d", m_Flags); Game::entityManager->SerializeEntity(m_Parent); @@ -1057,7 +1038,7 @@ void PetComponent::Activate(Item* item, bool registerPet, bool fromTaming) { // auto* const owner = GetOwner(); if (!owner) return; - SetFlag(PetFlag::SPAWNING); + m_Flags.Set(PetFlag::SPAWNING); auto databaseData = inventoryComponent->GetDatabasePet(m_DatabaseId); @@ -1203,39 +1184,11 @@ void PetComponent::Command(const NiPoint3& position, const LWOOBJID source, cons } } -LWOOBJID PetComponent::GetOwnerId() const { - return m_Owner; -} - Entity* PetComponent::GetOwner() const { return Game::entityManager->GetEntity(m_Owner); } -LWOOBJID PetComponent::GetDatabaseId() const { - return m_DatabaseId; -} - -LWOOBJID PetComponent::GetInteraction() const { - return m_Interaction; -} - -LWOOBJID PetComponent::GetItemId() const { - return m_ItemId; -} - -ePetAbilityType PetComponent::GetAbility() const { - return m_Ability; -} - -void PetComponent::SetInteraction(LWOOBJID value) { - m_Interaction = value; -} - -void PetComponent::SetAbility(ePetAbilityType value) { - m_Ability = value; -} - -PetComponent* PetComponent::GetTamingPet(LWOOBJID tamer) { +PetComponent* PetComponent::GetTamingPet(const LWOOBJID tamer) { const auto& pair = currentActivities.find(tamer); if (pair == currentActivities.end()) { @@ -1253,7 +1206,7 @@ PetComponent* PetComponent::GetTamingPet(LWOOBJID tamer) { return entity->GetComponent(); } -PetComponent* PetComponent::GetActivePet(LWOOBJID owner) { +PetComponent* PetComponent::GetActivePet(const LWOOBJID owner) { const auto& pair = activePets.find(owner); if (pair == activePets.end()) { @@ -1271,13 +1224,6 @@ PetComponent* PetComponent::GetActivePet(LWOOBJID owner) { return entity->GetComponent(); } -Entity* PetComponent::GetParentEntity() const { - return m_Parent; -} - -PetComponent::~PetComponent() { -} - void PetComponent::SetPetNameForModeration(const std::string& petName) { int approved = 1; //default, in mod @@ -1291,7 +1237,7 @@ void PetComponent::SetPetNameForModeration(const std::string& petName) { } void PetComponent::LoadPetNameFromModeration() { - auto petNameInfo = Database::Get()->GetPetNameInfo(m_DatabaseId); + const auto petNameInfo = Database::Get()->GetPetNameInfo(m_DatabaseId); if (petNameInfo) { m_ModerationStatus = petNameInfo->approvalStatus; if (m_ModerationStatus == 2) { @@ -1299,7 +1245,3 @@ void PetComponent::LoadPetNameFromModeration() { } } } - -void PetComponent::SetPreconditions(std::string& preconditions) { - m_Preconditions = new PreconditionExpression(preconditions); -} diff --git a/dGame/dComponents/PetComponent.h b/dGame/dComponents/PetComponent.h index 568b86c01..7244d5cb5 100644 --- a/dGame/dComponents/PetComponent.h +++ b/dGame/dComponents/PetComponent.h @@ -2,6 +2,7 @@ #define PETCOMPONENT_H #include "Entity.h" +#include "Flag.h" #include "MovementAIComponent.h" #include "Component.h" #include "Preconditions.h" @@ -11,6 +12,13 @@ #include "CDPetComponentTable.h" #include "CDClientManager.h" +#include +#include + +// Forward declarations +class PetTest; +class PetComponentFlagTest; + /* * The current state of the pet AI */ @@ -37,44 +45,18 @@ enum class PetInteractType : uint8_t { */ enum class PetFlag : uint32_t { NONE, - IDLE = 1 << 0, //0x01 - Seems to be "idle," which the game doesn't differentiate from "follow" - UNKNOWN2 = 1 << 1, //0x02, - UNKNOWN4 = 1 << 2, //0x04 - FOLLOWING(?) - BEING_TAMED = 1 << 4, //0x10, - NOT_WAITING = 1 << 5, //0x20, - IMMOBILE = 1 << 6, //0x40 - Seems to be the "stop moving" flag - called when taming begins and stays active until a name is submitted - SPAWNING = 1 << 7, //0x80 - ON_SWITCH = 1 << 8, //0x100 - UNKNOWN1024 = 1 << 10, //0x400 - TAMEABLE = 1 << 26 //0x4000000 + IDLE, //0x01 - Seems to be "idle," which the game doesn't differentiate from "follow" + UNKNOWN2, //0x02, + UNKNOWN4, //0x04 - FOLLOWING(?) + BEING_TAMED, //0x10, + NOT_WAITING, //0x20, + IMMOBILE, //0x40 - Seems to be the "stop moving" flag - called when taming begins and stays active until a name is submitted + SPAWNING, //0x80 + ON_SWITCH, //0x100 + UNKNOWN1024 = 10, //0x400 + TAMEABLE = 26 //0x4000000 }; -/** - * Define bitwise operators for PetFlag (TODO: Encapsulate into proper class) -*/ -constexpr PetFlag operator|(const PetFlag lhs, const PetFlag rhs) { - using underlying_type = std::underlying_type_t; - return static_cast(static_cast(lhs) | static_cast(rhs)); -} - -constexpr PetFlag& operator|=(PetFlag& lhs, const PetFlag rhs) { - return lhs = lhs | rhs; -} - -constexpr PetFlag operator&(const PetFlag lhs, const PetFlag rhs) { - using underlying_type = std::underlying_type_t; - return static_cast(static_cast(lhs) & static_cast(rhs)); -} - -constexpr PetFlag& operator&=(PetFlag& lhs, const PetFlag rhs) { - return lhs = lhs & rhs; -} - -constexpr PetFlag operator~(const PetFlag flag) { - using underlying_type = std::underlying_type_t; - return static_cast(~static_cast(flag)); -} - /** * The pet emote animation ids that can used in PetComponent::Command() */ @@ -88,8 +70,7 @@ enum class PetEmote : int32_t { * Represents an entity that is a pet. This pet can be tamed and consequently follows the tamer around, allowing it * to dig for treasure and activate pet bouncers. */ -class PetComponent final : public Component -{ +class PetComponent final : public Component { public: static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::PET; @@ -99,8 +80,8 @@ class PetComponent final : public Component * @param componentId The component id */ explicit PetComponent(Entity* parentEntity, uint32_t componentId); - - ~PetComponent() override; + + ~PetComponent() override = default; /** * Serializes the pet @@ -113,56 +94,16 @@ class PetComponent final : public Component * Sets the AI state of the pet * @param newState New pet AI state */ - void SetPetAiState(PetAiState newState); + void SetPetAiState(const PetAiState newState) noexcept { + m_State = newState; + }; /** * Gets the AI state of the pet */ - PetAiState GetPetAiState() const { return m_State; } - - /** - * Sets one or more pet flags - * @param flag PetFlag(s) to set - */ - template - void SetFlag(varArg... flag) { - m_Flags |= (flag | ...); - } - - /** - * Sets the pet to ONLY have the specified flag(s), clearing all others - * @param flag PetFlag(s) to set exclusively - */ - template - void SetOnlyFlag(varArg... flag) { - m_Flags = (flag | ...); - } - - /** - * Unsets one or more pet flags - * @param flag PetFlag(s) to unset - */ - template - void UnsetFlag(varArg... flag) { - m_Flags &= ~(flag | ...); - } - - /** - * Returns true if the pet has all the specified flag(s) - * @param flag PetFlag(s) to check - */ - template - bool HasFlag(varArg... flag) const { - return (m_Flags & (flag | ...)) == (flag | ...); - } - - /** - * Returns true if the pet has ONLY the specified flag(s) - * @param flag PetFlag(s) to check if the pet has exclusively - */ - template - bool HasOnlyFlag(varArg... flag) const { - return m_Flags == (flag | ...); + [[nodiscard]] + PetAiState GetPetAiState() const noexcept { + return m_State; } /** @@ -238,7 +179,7 @@ class PetComponent final : public Component /** * Start a pet interaction with an object at a given position */ - void StartInteract(const NiPoint3& position, const PetInteractType interactType, const LWOOBJID& interactID); + void StartInteract(const NiPoint3& position, const PetInteractType interactionType, const LWOOBJID& interactID); /** * Stop a pet interaction with an object @@ -246,16 +187,6 @@ class PetComponent final : public Component */ void StopInteract(bool bDontSerialize = false); - /** - * Set the type of interaction the pet is executing - */ - void SetInteractType(PetInteractType interactType) { m_InteractType = interactType; }; - - /** - * Get the type of interaction the pet is executing - */ - PetInteractType GetInteractType() { return m_InteractType; }; - /** * Spawns a pet from an item in the inventory of an owner * @param item the item to create the pet from @@ -287,90 +218,75 @@ class PetComponent final : public Component * Returns the ID of the owner of this pet (if any) * @return the ID of the owner of this pet */ - LWOOBJID GetOwnerId() const; + [[nodiscard]] + LWOOBJID GetOwnerId() const noexcept { + return m_Owner; + }; /** * Returns the entity that owns this pet (if any) * @return the entity that owns this pet */ + [[nodiscard]] Entity* GetOwner() const; /** * Returns the ID that is stored in the database with regards to this pet, only set for pets that are tamed * @return the ID that is stored in the database with regards to this pet */ - LWOOBJID GetDatabaseId() const; - - /** - * Returns the ID of the object that the pet is currently interacting with, could be a treasure chest or a switch - * @return the ID of the object that the pet is currently interacting with - */ - LWOOBJID GetInteraction() const; - - /** - * Sets the ID that the pet is interacting with - * @param value the ID that the pet is interacting with - */ - void SetInteraction(LWOOBJID value); + [[nodiscard]] + LWOOBJID GetDatabaseId() const noexcept { + return m_DatabaseId; + } /** * Returns the ID that this pet was spawned from, only set for tamed pets * @return the ID that this pet was spawned from */ - LWOOBJID GetItemId() const; - - /** - * Returns the status of this pet, e.g. tamable or tamed. The values here are still a bit of mystery and likely a - * bit map - * @return the status of this pet - */ - /*uint32_t GetStatus() const;*/ - - /** - * Sets the current status of the pet - * @param value the current status of the pet to set - */ - /*void SetStatus(uint32_t value);*/ - - /** - * Returns an ability the pet may perform, currently unused - * @return an ability the pet may perform - */ - ePetAbilityType GetAbility() const; - - /** - * Sets the ability of the pet, currently unused - * @param value the ability to set - */ - void SetAbility(ePetAbilityType value); + [[nodiscard]] + LWOOBJID GetItemId() const noexcept { + return m_ItemId; + } /** * Sets preconditions for the pet that need to be met before it can be tamed * @param conditions the preconditions to set */ - void SetPreconditions(std::string& conditions); + void SetPreconditions(const std::string& preconditions) { + m_Preconditions = PreconditionExpression(preconditions); + } /** * Sets if the pet is ready to interact with an object * @param isReady whether the pet is ready to interact (true) or not (false) */ - void SetIsReadyToInteract(bool isReady) { m_ReadyToInteract = isReady; }; + void SetIsReadyToInteract(const bool isReady) { + m_ReadyToInteract = isReady; + }; /** * @return is pet ready to interact with an object */ - bool IsReadyToInteract() { return m_ReadyToInteract; }; + [[nodiscard]] + bool IsReadyToInteract() const noexcept { + return m_ReadyToInteract; + } /** * Sets if the pet is currently handling an interaction with an object * @param isHandlingInteraction whether the pet is currently handling an interaction with an object */ - void SetIsHandlingInteraction(bool isHandlingInteraction) { m_IsHandlingInteraction = isHandlingInteraction; }; + void SetIsHandlingInteraction(const bool isHandlingInteraction) { + m_IsHandlingInteraction = isHandlingInteraction; + } /** * @return is pet currently handling an interaction with an object */ - bool IsHandlingInteraction() { return m_IsHandlingInteraction; }; + [[nodiscard]] + bool IsHandlingInteraction() const noexcept { + return m_IsHandlingInteraction; + }; /** * Set up the pet bouncer interaction @@ -406,7 +322,10 @@ class PetComponent final : public Component * Returns the entity that this component belongs to * @return the entity that this component belongs to */ - Entity* GetParentEntity() const; + [[nodiscard]] + Entity* GetParentEntity() const noexcept { + return m_Parent; + } /** * Sets the name of the pet to be moderated @@ -425,6 +344,7 @@ class PetComponent final : public Component * @param tamer the entity that's currently taming * @return the pet component of the entity that's being tamed */ + [[nodiscard]] static PetComponent* GetTamingPet(LWOOBJID tamer); /** @@ -432,6 +352,7 @@ class PetComponent final : public Component * @param owner the owner of the pet that's spawned * @return the pet component of the entity that was spawned by the owner */ + [[nodiscard]] static PetComponent* GetActivePet(LWOOBJID owner); /** @@ -443,11 +364,15 @@ class PetComponent final : public Component void AddDrainImaginationTimer(Item* item, bool fromTaming = false); private: + // Needed so these can access flags + friend class DamagingPets; + friend class PetTest; + FRIEND_TEST(PetTest, PetComponentFlagTest); /** * Information for the minigame to be completed */ - struct PetPuzzleData + struct PuzzleData { /** * The LOT of the object that is to be created @@ -475,6 +400,28 @@ class PetComponent final : public Component int32_t numValidPieces; }; + struct Interaction { + /** + * The type of object that the pet is currently interacting with (e.g. a treasure chest or switch) + */ + PetInteractType type = PetInteractType::none; + + /** + * The interaction ability + */ + ePetAbilityType ability = ePetAbilityType::Invalid; + + /** + * The ID of the object that the pet is currently interacting with (e.g. a treasure chest or switch) + */ + LWOOBJID obj = LWOOBJID_EMPTY; + }; + + /** + * Pet interaction info + */ + Interaction m_Interaction{}; + /** * Cache of all the pets that are currently spawned, indexed by tamer */ @@ -488,7 +435,7 @@ class PetComponent final : public Component /** * Cache of all the minigames and their information from the database */ - static std::unordered_map buildCache; + static std::unordered_map buildCache; /** * Flags that indicate that a player has tamed a pet, indexed by the LOT of the pet @@ -508,32 +455,22 @@ class PetComponent final : public Component /** * The ID of the model that was built to complete the taming minigame for this pet */ - LWOOBJID m_ModelId; - - /** - * The ID of the object that the pet is currently interacting with (e.g. a treasure chest or switch) - */ - LWOOBJID m_Interaction; - - /** - * The type of object that the pet is currently interacting with (e.g. a treasure chest or switch) - */ - PetInteractType m_InteractType; + LWOOBJID m_ModelId = LWOOBJID_EMPTY; /** * The ID of the entity that owns this pet */ - LWOOBJID m_Owner; + LWOOBJID m_Owner = LWOOBJID_EMPTY; /** * The ID of the entity that is currently taming this pet */ - LWOOBJID m_Tamer; + LWOOBJID m_Tamer = LWOOBJID_EMPTY; /** * The ID under which this pet is stored in the database (if it's tamed) */ - LWOOBJID m_DatabaseId; + LWOOBJID m_DatabaseId = LWOOBJID_EMPTY; /** * The ID of the item from which this pet was created @@ -543,7 +480,7 @@ class PetComponent final : public Component /** * The moderation status for the name of this pet */ - uint32_t m_ModerationStatus; + uint32_t m_ModerationStatus = 0; /** * The name of this pet @@ -558,32 +495,27 @@ class PetComponent final : public Component /** * The current flags of the pet (e.g. tamable, tamed, etc). */ - PetFlag m_Flags; + Flag m_Flags; /** * The current state of the pet AI */ PetAiState m_State; - /** - * A currently active ability, mostly unused - */ - ePetAbilityType m_Ability; - /** * The time an entity has left to complete the minigame */ - float m_Timer; + float m_Timer = 0; /** * A timer that tracks how long a tamed pet has been to far away from its owner, triggering a teleport after timeout */ - float m_TimerAway; + float m_TimerAway = 0; /** * A timer that tracks how long until a tamed pet will bounce again when standing over a treasure dig site */ - float m_TimerBounce; + float m_TimerBounce = 0; /** * Boolean that sets if a pet is ready to interact with an object @@ -608,7 +540,7 @@ class PetComponent final : public Component /** * Preconditions that need to be met before an entity can tame this pet */ - PreconditionExpression* m_Preconditions; + std::optional m_Preconditions; /** * Pet information loaded from the CDClientDatabase diff --git a/dScripts/02_server/Pets/DamagingPets.cpp b/dScripts/02_server/Pets/DamagingPets.cpp index 2702fa782..a289e261d 100644 --- a/dScripts/02_server/Pets/DamagingPets.cpp +++ b/dScripts/02_server/Pets/DamagingPets.cpp @@ -77,10 +77,10 @@ void DamagingPets::MakeUntamable(Entity* self) { auto* const petComponent = self->GetComponent(); // If the pet is currently not being tamed, make it hostile - if (petComponent != nullptr && !petComponent->HasFlag(PetFlag::NOT_WAITING)) { + if (petComponent != nullptr && !petComponent->m_Flags.Has(PetFlag::NOT_WAITING)) { self->SetNetworkVar(u"bIAmTamable", false); self->SetVar(u"IsEvil", true); - petComponent->SetFlag(PetFlag::IDLE); + petComponent->m_Flags.Set(PetFlag::IDLE); auto* combatAIComponent = self->GetComponent(); if (combatAIComponent != nullptr) { @@ -110,7 +110,7 @@ void DamagingPets::ClearEffects(Entity* self) { auto* petComponent = self->GetComponent(); if (petComponent != nullptr) { - petComponent->SetFlag(PetFlag::TAMEABLE, PetFlag::UNKNOWN2); + petComponent->m_Flags.Set(PetFlag::TAMEABLE, PetFlag::UNKNOWN2); } auto* combatAIComponent = self->GetComponent(); diff --git a/tests/dCommonTests/CMakeLists.txt b/tests/dCommonTests/CMakeLists.txt index ef7c4cba9..b83f4ddd8 100644 --- a/tests/dCommonTests/CMakeLists.txt +++ b/tests/dCommonTests/CMakeLists.txt @@ -1,6 +1,7 @@ set(DCOMMONTEST_SOURCES "AMFDeserializeTests.cpp" "Amf3Tests.cpp" + "FlagTests.cpp" "ToUnderlyingTests.cpp" "HeaderSkipTest.cpp" "TestCDFeatureGatingTable.cpp" diff --git a/tests/dCommonTests/FlagTests.cpp b/tests/dCommonTests/FlagTests.cpp new file mode 100644 index 000000000..0e9f5acd0 --- /dev/null +++ b/tests/dCommonTests/FlagTests.cpp @@ -0,0 +1,67 @@ +#include +#include "Flag.h" + +enum class TestFlag : uint8_t { + FLAG0, + FLAG1, + FLAG2, + FLAG3, + FLAG4 +}; + +/** + * Test bitset flags +*/ +TEST(FlagTests, FlagMethodTest) { + using enum TestFlag; + + auto flag = Flag{}; + + // Test setting and reading single flags, exclusively + flag.Reset(FLAG0); + ASSERT_TRUE(flag.HasOnly(FLAG0)); + flag.Reset(FLAG2); + ASSERT_TRUE(flag.HasOnly(FLAG2)); + ASSERT_FALSE(flag.HasOnly(FLAG1)); + + // Test setting and reading multiple flags, exclusively + flag.Reset(FLAG3, FLAG1); + ASSERT_FALSE(flag.Has(FLAG2)); + ASSERT_TRUE(flag.Has(FLAG3)); + ASSERT_TRUE(flag.Has(FLAG1)); + ASSERT_TRUE(flag.Has(FLAG3, FLAG1)); + ASSERT_FALSE(flag.HasOnly(FLAG3)); + ASSERT_FALSE(flag.HasOnly(FLAG1)); + ASSERT_TRUE(flag.HasOnly(FLAG3, FLAG1)); + + // Test flags are being properly reset for next batch of tests + flag.Reset(FLAG0); + ASSERT_TRUE(flag.Has(FLAG0)); + ASSERT_TRUE(flag.HasOnly(FLAG0)); + ASSERT_FALSE(flag.Has(FLAG3)); + ASSERT_FALSE(flag.Has(FLAG3, FLAG1, FLAG2)); + + // Test setting and reading single flags, non-exclusively + flag.Set(FLAG3); + ASSERT_TRUE(flag.Has(FLAG3)); + ASSERT_FALSE(flag.Has(FLAG1)); + + // Test setting and reading multiple flags, non-exclusively + flag.Set(FLAG2, FLAG4); + ASSERT_TRUE(flag.Has(FLAG2, FLAG4)); + ASSERT_TRUE(flag.Has(FLAG3)); + ASSERT_FALSE(flag.Has(FLAG3, FLAG1)); + ASSERT_TRUE(flag.Has(FLAG3, FLAG2, FLAG4)); + ASSERT_FALSE(flag.Has(FLAG1)); + ASSERT_FALSE(flag.Has(FLAG1, FLAG3, FLAG2, FLAG4)); + + // Test unsetting and reading multiple flags, non-exclusively + flag.Unset(FLAG3, FLAG1); + ASSERT_FALSE(flag.Has(FLAG3, FLAG1)); + ASSERT_FALSE(flag.Has(FLAG3)); + ASSERT_TRUE(flag.Has(FLAG2, FLAG4)); + ASSERT_FALSE(flag.Has(FLAG3, FLAG2, FLAG4)); + ASSERT_FALSE(flag.Has(FLAG1)); + ASSERT_FALSE(flag.Has(FLAG1, FLAG3, FLAG2, FLAG4)); +} + diff --git a/tests/dGameTests/dComponentsTests/PetComponentTests.cpp b/tests/dGameTests/dComponentsTests/PetComponentTests.cpp index 05eeab752..295b37d51 100644 --- a/tests/dGameTests/dComponentsTests/PetComponentTests.cpp +++ b/tests/dGameTests/dComponentsTests/PetComponentTests.cpp @@ -31,21 +31,6 @@ class PetTest : public GameDependenciesTest { } }; -TEST_F(PetTest, PlacementNewAddComponentTest) { - using enum PetFlag; - - // Test adding component - ASSERT_NE(petComponent, nullptr); - baseEntity->AddComponent(1); - ASSERT_NE(baseEntity->GetComponent(), nullptr); - - // Test getting initial status - ASSERT_EQ(petComponent->GetParent()->GetObjectID(), 15); - ASSERT_TRUE(petComponent->HasFlag(NONE)); - ASSERT_EQ(petComponent->GetPetAiState(), PetAiState::spawn); - ASSERT_EQ(petComponent->GetAbility(), ePetAbilityType::Invalid); -} - /** * Test bitset pet flags */ @@ -53,53 +38,67 @@ TEST_F(PetTest, PetComponentFlagTest) { using enum PetFlag; // Test setting and reading single flags, exclusively - petComponent->SetOnlyFlag(NONE); - ASSERT_TRUE(petComponent->HasOnlyFlag(NONE)); - petComponent->SetOnlyFlag(TAMEABLE); - ASSERT_TRUE(petComponent->HasOnlyFlag(TAMEABLE)); - ASSERT_FALSE(petComponent->HasOnlyFlag(SPAWNING)); + petComponent->m_Flags.Reset(NONE); + ASSERT_TRUE(petComponent->m_Flags.HasOnly(NONE)); + petComponent->m_Flags.Reset(TAMEABLE); + ASSERT_TRUE(petComponent->m_Flags.HasOnly(TAMEABLE)); + ASSERT_FALSE(petComponent->m_Flags.HasOnly(SPAWNING)); // Test setting and reading multiple flags, exclusively - petComponent->SetOnlyFlag(NOT_WAITING, SPAWNING); - ASSERT_FALSE(petComponent->HasFlag(TAMEABLE)); - ASSERT_TRUE(petComponent->HasFlag(NOT_WAITING)); - ASSERT_TRUE(petComponent->HasFlag(SPAWNING)); - ASSERT_TRUE(petComponent->HasFlag(NOT_WAITING, SPAWNING)); - ASSERT_FALSE(petComponent->HasOnlyFlag(NOT_WAITING)); - ASSERT_FALSE(petComponent->HasOnlyFlag(SPAWNING)); - ASSERT_TRUE(petComponent->HasOnlyFlag(NOT_WAITING, SPAWNING)); + petComponent->m_Flags.Reset(NOT_WAITING, SPAWNING); + ASSERT_FALSE(petComponent->m_Flags.Has(TAMEABLE)); + ASSERT_TRUE(petComponent->m_Flags.Has(NOT_WAITING)); + ASSERT_TRUE(petComponent->m_Flags.Has(SPAWNING)); + ASSERT_TRUE(petComponent->m_Flags.Has(NOT_WAITING, SPAWNING)); + ASSERT_FALSE(petComponent->m_Flags.HasOnly(NOT_WAITING)); + ASSERT_FALSE(petComponent->m_Flags.HasOnly(SPAWNING)); + ASSERT_TRUE(petComponent->m_Flags.HasOnly(NOT_WAITING, SPAWNING)); // Test flags are being properly reset for next batch of tests - petComponent->SetOnlyFlag(NONE); - ASSERT_TRUE(petComponent->HasFlag(NONE)); - ASSERT_TRUE(petComponent->HasOnlyFlag(NONE)); - ASSERT_FALSE(petComponent->HasFlag(NOT_WAITING)); - ASSERT_FALSE(petComponent->HasFlag(NOT_WAITING, SPAWNING, TAMEABLE)); + petComponent->m_Flags.Reset(NONE); + ASSERT_TRUE(petComponent->m_Flags.Has(NONE)); + ASSERT_TRUE(petComponent->m_Flags.HasOnly(NONE)); + ASSERT_FALSE(petComponent->m_Flags.Has(NOT_WAITING)); + ASSERT_FALSE(petComponent->m_Flags.Has(NOT_WAITING, SPAWNING, TAMEABLE)); // Test setting and reading single flags, non-exclusively - petComponent->SetFlag(NOT_WAITING); - ASSERT_TRUE(petComponent->HasFlag(NOT_WAITING)); - ASSERT_FALSE(petComponent->HasFlag(SPAWNING)); + petComponent->m_Flags.Set(NOT_WAITING); + ASSERT_TRUE(petComponent->m_Flags.Has(NOT_WAITING)); + ASSERT_FALSE(petComponent->m_Flags.Has(SPAWNING)); // Test setting and reading multiple flags, non-exclusively - petComponent->SetFlag(TAMEABLE, BEING_TAMED); - ASSERT_TRUE(petComponent->HasFlag(TAMEABLE, BEING_TAMED)); - ASSERT_TRUE(petComponent->HasFlag(NOT_WAITING)); - ASSERT_FALSE(petComponent->HasFlag(NOT_WAITING, SPAWNING)); - ASSERT_TRUE(petComponent->HasFlag(NOT_WAITING, TAMEABLE, BEING_TAMED)); - ASSERT_FALSE(petComponent->HasFlag(SPAWNING)); - ASSERT_FALSE(petComponent->HasFlag(SPAWNING, NOT_WAITING, TAMEABLE, BEING_TAMED)); + petComponent->m_Flags.Set(TAMEABLE, BEING_TAMED); + ASSERT_TRUE(petComponent->m_Flags.Has(TAMEABLE, BEING_TAMED)); + ASSERT_TRUE(petComponent->m_Flags.Has(NOT_WAITING)); + ASSERT_FALSE(petComponent->m_Flags.Has(NOT_WAITING, SPAWNING)); + ASSERT_TRUE(petComponent->m_Flags.Has(NOT_WAITING, TAMEABLE, BEING_TAMED)); + ASSERT_FALSE(petComponent->m_Flags.Has(SPAWNING)); + ASSERT_FALSE(petComponent->m_Flags.Has(SPAWNING, NOT_WAITING, TAMEABLE, BEING_TAMED)); // Test unsetting and reading multiple flags, non-exclusively - petComponent->UnsetFlag(NOT_WAITING, SPAWNING); - ASSERT_FALSE(petComponent->HasFlag(NOT_WAITING, SPAWNING)); - ASSERT_FALSE(petComponent->HasFlag(NOT_WAITING)); - ASSERT_TRUE(petComponent->HasFlag(TAMEABLE, BEING_TAMED)); - ASSERT_FALSE(petComponent->HasFlag(NOT_WAITING, TAMEABLE, BEING_TAMED)); - ASSERT_FALSE(petComponent->HasFlag(SPAWNING)); - ASSERT_FALSE(petComponent->HasFlag(SPAWNING, NOT_WAITING, TAMEABLE, BEING_TAMED)); + petComponent->m_Flags.Unset(NOT_WAITING, SPAWNING); + ASSERT_FALSE(petComponent->m_Flags.Has(NOT_WAITING, SPAWNING)); + ASSERT_FALSE(petComponent->m_Flags.Has(NOT_WAITING)); + ASSERT_TRUE(petComponent->m_Flags.Has(TAMEABLE, BEING_TAMED)); + ASSERT_FALSE(petComponent->m_Flags.Has(NOT_WAITING, TAMEABLE, BEING_TAMED)); + ASSERT_FALSE(petComponent->m_Flags.Has(SPAWNING)); + ASSERT_FALSE(petComponent->m_Flags.Has(SPAWNING, NOT_WAITING, TAMEABLE, BEING_TAMED)); } +TEST_F(PetTest, PlacementNewAddComponentTest) { + using enum PetFlag; + + // Test adding component + ASSERT_NE(petComponent, nullptr); + baseEntity->AddComponent(1); + ASSERT_NE(baseEntity->GetComponent(), nullptr); + + // Test getting initial status + ASSERT_EQ(petComponent->GetParent()->GetObjectID(), 15); + ASSERT_EQ(petComponent->GetPetAiState(), PetAiState::spawn); +} + + TEST_F(PetTest, PetAiState) { const auto initialState = petComponent->GetPetAiState(); ASSERT_EQ(initialState, PetAiState::spawn); @@ -126,9 +125,6 @@ TEST_F(PetTest, PetUse) { // Test bouncer logic ASSERT_FALSE(petComponent->IsHandlingInteraction()); - petComponent->SetAbility(ePetAbilityType::JumpOnObject); - ASSERT_EQ(petComponent->GetAbility(), ePetAbilityType::JumpOnObject); - petComponent->SetInteractType(PetInteractType::bouncer); petComponent->OnUse(baseEntity); // need to add a destroyable component to the test entity and test the imagination drain From 6ddf5b3e92cfcc5306ca2d263fb5162da258bd00 Mon Sep 17 00:00:00 2001 From: jadebenn Date: Sat, 20 Apr 2024 00:07:08 -0500 Subject: [PATCH 60/73] remove unneeded tests --- .../CDClientTables/CDPetComponentTable.cpp | 2 +- dGame/dComponents/PetComponent.cpp | 3 +- dGame/dComponents/PetComponent.h | 14 ++--- .../dComponentsTests/PetComponentTests.cpp | 54 ------------------- 4 files changed, 6 insertions(+), 67 deletions(-) diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDPetComponentTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDPetComponentTable.cpp index 078a5b101..96d4550c0 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDPetComponentTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDPetComponentTable.cpp @@ -54,7 +54,7 @@ void CDPetComponentTable::LoadValuesFromDefaults() { } const CDPetComponent& CDPetComponentTable::GetByID(const uint32_t componentID) { - const auto& entries = GetEntriesMutable(); + const auto& entries = GetEntries(); const auto itr = entries.find(componentID); if (itr == entries.cend()) { LOG("Unable to load pet component (ID %i) values from database! Using default values instead.", componentID); diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index bc64f3be0..292d8a3fc 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -96,7 +96,8 @@ PetComponent::PetComponent(Entity* parentEntity, uint32_t componentId) : Compone void PetComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) { const bool tamed = m_Owner != LWOOBJID_EMPTY; - outBitStream.Write1(); // Always serialize as dirty for now + constexpr bool isDirty = true; + outBitStream.Write(isDirty); // Always serialize as dirty for now outBitStream.Write(m_Flags); outBitStream.Write(tamed ? m_Interaction.ability : ePetAbilityType::Invalid); // Something with the overhead icon? diff --git a/dGame/dComponents/PetComponent.h b/dGame/dComponents/PetComponent.h index 7244d5cb5..a9f6b44c2 100644 --- a/dGame/dComponents/PetComponent.h +++ b/dGame/dComponents/PetComponent.h @@ -13,11 +13,6 @@ #include "CDClientManager.h" #include -#include - -// Forward declarations -class PetTest; -class PetComponentFlagTest; /* * The current state of the pet AI @@ -364,16 +359,13 @@ class PetComponent final : public Component { void AddDrainImaginationTimer(Item* item, bool fromTaming = false); private: - // Needed so these can access flags - friend class DamagingPets; - friend class PetTest; - FRIEND_TEST(PetTest, PetComponentFlagTest); + // Needed so it can access flags + friend class DamagingPets; /** * Information for the minigame to be completed */ - struct PuzzleData - { + struct PuzzleData { /** * The LOT of the object that is to be created */ diff --git a/tests/dGameTests/dComponentsTests/PetComponentTests.cpp b/tests/dGameTests/dComponentsTests/PetComponentTests.cpp index 295b37d51..31633303f 100644 --- a/tests/dGameTests/dComponentsTests/PetComponentTests.cpp +++ b/tests/dGameTests/dComponentsTests/PetComponentTests.cpp @@ -31,60 +31,6 @@ class PetTest : public GameDependenciesTest { } }; -/** - * Test bitset pet flags -*/ -TEST_F(PetTest, PetComponentFlagTest) { - using enum PetFlag; - - // Test setting and reading single flags, exclusively - petComponent->m_Flags.Reset(NONE); - ASSERT_TRUE(petComponent->m_Flags.HasOnly(NONE)); - petComponent->m_Flags.Reset(TAMEABLE); - ASSERT_TRUE(petComponent->m_Flags.HasOnly(TAMEABLE)); - ASSERT_FALSE(petComponent->m_Flags.HasOnly(SPAWNING)); - - // Test setting and reading multiple flags, exclusively - petComponent->m_Flags.Reset(NOT_WAITING, SPAWNING); - ASSERT_FALSE(petComponent->m_Flags.Has(TAMEABLE)); - ASSERT_TRUE(petComponent->m_Flags.Has(NOT_WAITING)); - ASSERT_TRUE(petComponent->m_Flags.Has(SPAWNING)); - ASSERT_TRUE(petComponent->m_Flags.Has(NOT_WAITING, SPAWNING)); - ASSERT_FALSE(petComponent->m_Flags.HasOnly(NOT_WAITING)); - ASSERT_FALSE(petComponent->m_Flags.HasOnly(SPAWNING)); - ASSERT_TRUE(petComponent->m_Flags.HasOnly(NOT_WAITING, SPAWNING)); - - // Test flags are being properly reset for next batch of tests - petComponent->m_Flags.Reset(NONE); - ASSERT_TRUE(petComponent->m_Flags.Has(NONE)); - ASSERT_TRUE(petComponent->m_Flags.HasOnly(NONE)); - ASSERT_FALSE(petComponent->m_Flags.Has(NOT_WAITING)); - ASSERT_FALSE(petComponent->m_Flags.Has(NOT_WAITING, SPAWNING, TAMEABLE)); - - // Test setting and reading single flags, non-exclusively - petComponent->m_Flags.Set(NOT_WAITING); - ASSERT_TRUE(petComponent->m_Flags.Has(NOT_WAITING)); - ASSERT_FALSE(petComponent->m_Flags.Has(SPAWNING)); - - // Test setting and reading multiple flags, non-exclusively - petComponent->m_Flags.Set(TAMEABLE, BEING_TAMED); - ASSERT_TRUE(petComponent->m_Flags.Has(TAMEABLE, BEING_TAMED)); - ASSERT_TRUE(petComponent->m_Flags.Has(NOT_WAITING)); - ASSERT_FALSE(petComponent->m_Flags.Has(NOT_WAITING, SPAWNING)); - ASSERT_TRUE(petComponent->m_Flags.Has(NOT_WAITING, TAMEABLE, BEING_TAMED)); - ASSERT_FALSE(petComponent->m_Flags.Has(SPAWNING)); - ASSERT_FALSE(petComponent->m_Flags.Has(SPAWNING, NOT_WAITING, TAMEABLE, BEING_TAMED)); - - // Test unsetting and reading multiple flags, non-exclusively - petComponent->m_Flags.Unset(NOT_WAITING, SPAWNING); - ASSERT_FALSE(petComponent->m_Flags.Has(NOT_WAITING, SPAWNING)); - ASSERT_FALSE(petComponent->m_Flags.Has(NOT_WAITING)); - ASSERT_TRUE(petComponent->m_Flags.Has(TAMEABLE, BEING_TAMED)); - ASSERT_FALSE(petComponent->m_Flags.Has(NOT_WAITING, TAMEABLE, BEING_TAMED)); - ASSERT_FALSE(petComponent->m_Flags.Has(SPAWNING)); - ASSERT_FALSE(petComponent->m_Flags.Has(SPAWNING, NOT_WAITING, TAMEABLE, BEING_TAMED)); -} - TEST_F(PetTest, PlacementNewAddComponentTest) { using enum PetFlag; From 5ddf4ce28b00ffe125dc6afdf3feb3b06ca43b90 Mon Sep 17 00:00:00 2001 From: jadebenn Date: Sat, 20 Apr 2024 11:23:35 -0500 Subject: [PATCH 61/73] fix unused expression --- dGame/dComponents/PetComponent.cpp | 4 ++-- dGame/dComponents/PetComponent.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index 292d8a3fc..19013620b 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -826,8 +826,8 @@ void PetComponent::OnInteract() { } } -void PetComponent::StartInteract(const NiPoint3& position, const PetInteractType interactionType, const LWOOBJID& interactID) { - m_Interaction.obj; // TODO: Check if this should be serialized for goToObj +void PetComponent::StartInteract(const NiPoint3 position, const PetInteractType interactionType, const LWOOBJID interactID) { + m_Interaction.obj = interactID; // TODO: Check if this should be serialized for goToObj m_Interaction.type = interactionType; m_Interaction.ability = ePetAbilityType::GoToObject; SetPetAiState(PetAiState::goToObj); diff --git a/dGame/dComponents/PetComponent.h b/dGame/dComponents/PetComponent.h index a9f6b44c2..cdde1297b 100644 --- a/dGame/dComponents/PetComponent.h +++ b/dGame/dComponents/PetComponent.h @@ -174,7 +174,7 @@ class PetComponent final : public Component { /** * Start a pet interaction with an object at a given position */ - void StartInteract(const NiPoint3& position, const PetInteractType interactionType, const LWOOBJID& interactID); + void StartInteract(const NiPoint3 position, const PetInteractType interactionType, const LWOOBJID interactID); /** * Stop a pet interaction with an object From f41321eb7b8b3346779fce8a1bb1f4badb671420 Mon Sep 17 00:00:00 2001 From: jadebenn Date: Sat, 20 Apr 2024 14:17:34 -0500 Subject: [PATCH 62/73] Use compile-time flag setting --- dCommon/Flag.h | 121 ++++++++++++++++++----- dGame/dComponents/PetComponent.cpp | 58 +++++------ dGame/dComponents/PetComponent.h | 4 +- dGame/dGameMessages/GameMessages.cpp | 8 +- dGame/dGameMessages/GameMessages.h | 2 +- dScripts/02_server/Pets/DamagingPets.cpp | 6 +- tests/dCommonTests/FlagTests.cpp | 61 +++++++++++- 7 files changed, 196 insertions(+), 64 deletions(-) diff --git a/dCommon/Flag.h b/dCommon/Flag.h index a26cbc69b..d76725d03 100644 --- a/dCommon/Flag.h +++ b/dCommon/Flag.h @@ -1,81 +1,158 @@ #include "GeneralUtils.h" -#include -// TODO: Test bounds checking - +/** + * A bitset flag class capable of compile-time bounds-checking. + * It should be possible to unify its methods with C++23, but until then + * use the compile-time overloads preferentially. +*/ template requires std::is_enum_v class Flag { public: + // Type aliases using type = T; using underlying_type = std::underlying_type_t; - static constexpr auto MAX_FLAG_VAL = std::bit_width(sizeof(underlying_type)); + + // Static constants + static constexpr auto MAX_FLAG_VAL = sizeof(underlying_type) * CHAR_BIT; + + // Constructors + Flag() = default; + + // Run-time constructor + constexpr Flag(const T value) : m_Flags{ ConvertFlag(value) } {} /** - * Sets one or more flags + * RUNTIME: Sets one or more flags * @param flag Flag(s) to set */ template ... varArg> - constexpr void Set(varArg... flag) noexcept { + constexpr void Set(const varArg... flag) noexcept { m_Flags |= (ConvertFlag(flag) | ...); } /** - * Sets ONLY have the specified flag(s), clearing all others + * COMPILETIME: Sets one or more flags + * @param flag Flag(s) to set + */ + template + constexpr void Set() noexcept { + m_Flags |= (ConvertFlag() | ...); + } + + /** + * RUNTIME: Sets ONLY have the specified flag(s), clearing all others * @param flag Flag(s) to set exclusively */ template ... varArg> - constexpr void Reset(varArg... flag) { + constexpr void Reset(const varArg... flag) { m_Flags = (ConvertFlag(flag) | ...); } /** - * Unsets one or more flags + * COMPILETIME: Sets ONLY have the specified flag(s), clearing all others + * @param flag Flag(s) to set exclusively + */ + template + constexpr void Reset() noexcept { + m_Flags = (ConvertFlag() | ...); + } + + /** + * RUNTIME: Unsets one or more flags * @param flag Flag(s) to unset */ template ... varArg> - constexpr void Unset(varArg... flag) { + constexpr void Unset(const varArg... flag) { m_Flags &= ~(ConvertFlag(flag) | ...); } /** - * Returns true all the specified flag(s) are set + * COMPILETIME: Unsets one or more flags + * @param flag Flag(s) to unset + */ + template + constexpr void Unset() noexcept { + m_Flags &= ~(ConvertFlag() | ...); + } + + /** + * RUNTIME: Returns true all the specified flag(s) are set * @param flag Flag(s) to check */ template ... varArg> - constexpr bool Has(varArg... flag) const { + [[nodiscard]] + constexpr bool Has(const varArg... flag) const { return (m_Flags & (ConvertFlag(flag) | ...)) == (ConvertFlag(flag) | ...); } /** - * Returns true ONLY the specified flag(s) are set + * COMPILETIME: Returns true if all the specified flag(s) are set + * @param flag Flag(s) to check + */ + template + [[nodiscard]] + constexpr bool Has() const noexcept { + return (m_Flags & (ConvertFlag() | ...)) == (ConvertFlag() | ...); + } + + /** + * RUNTIME: Returns true if ONLY the specified flag(s) are set * @param flag Flag(s) to check */ template ... varArg> - constexpr bool HasOnly(varArg... flag) const { + [[nodiscard]] + constexpr bool HasOnly(const varArg... flag) const { return m_Flags == (ConvertFlag(flag) | ...); } + /** + * COMPILETIME: Returns true if ONLY the specified flag(s) are set + * @param flag Flag(s) to check + */ + template + [[nodiscard]] + constexpr bool HasOnly() const noexcept { + return m_Flags == (ConvertFlag() | ...); + } + + /** + * @return the raw value of the flags set + */ + [[nodiscard]] + constexpr T Value() const noexcept { + return static_cast(m_Flags); + } + /** * Operator overload to allow for '=' assignment */ constexpr Flag& operator=(const T value) { - Reset(value); + m_Flags = ConvertFlag(value); return *this; } private: + template + [[nodiscard]] + static consteval underlying_type ConvertFlag() noexcept { + constexpr auto flag_val = GeneralUtils::ToUnderlying(flag); + static_assert(flag_val <= MAX_FLAG_VAL, "Flag value is too large to set!"); + + return flag_val != 0 ? 1 << flag_val : flag_val; + } + [[nodiscard]] static constexpr underlying_type ConvertFlag(const T flag) { auto flag_val = GeneralUtils::ToUnderlying(flag); - if (flag_val != 0) { - return 1 << flag_val; - } - // This should theoeretically be possible to do at compile time, but idk how - if (std::bit_width(flag_val) > MAX_FLAG_VAL) { - throw std::runtime_error{ "Enum value too large to be a flag!" }; + + // This is less-efficient than the compile-time check, but still works + // We can probably unify this and the above functions with C++23 and 'if consteval' + if (flag_val > MAX_FLAG_VAL) { + throw std::runtime_error{ "Flag value is too large to set!" }; } - return flag_val; + + return flag_val != 0 ? 1 << flag_val : flag_val; } underlying_type m_Flags; diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index 19013620b..0c867e21e 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -45,7 +45,7 @@ std::unordered_map PetComponent::activePets{}; * Maps all the pet lots to a flag indicating that the player has caught it. All basic pets have been guessed by ObjID * while the faction ones could be checked using their respective missions. */ -std::map PetComponent::petFlags{ +const std::map PetComponent::petFlags{ { 3050, 801 }, // Elephant { 3054, 803 }, // Cat { 3195, 806 }, // Triceratops @@ -75,10 +75,12 @@ std::map PetComponent::petFlags{ { 13067, 838 }, // Skeleton dragon }; -PetComponent::PetComponent(Entity* parentEntity, uint32_t componentId) : Component{ parentEntity } { +PetComponent::PetComponent(Entity* parentEntity, uint32_t componentId) + : Component{ parentEntity } + , m_Flags{ PetFlag::SPAWNING } +{ m_PetInfo = CDClientManager::GetTable()->GetByID(componentId); // TODO: Make reference when safe m_ComponentId = componentId; - m_Flags = PetFlag::SPAWNING; // Tameable m_StartPosition = m_Parent->GetPosition(); m_MovementAI = nullptr; @@ -99,7 +101,7 @@ void PetComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpd constexpr bool isDirty = true; outBitStream.Write(isDirty); // Always serialize as dirty for now - outBitStream.Write(m_Flags); + outBitStream.Write(m_Flags.Value()); outBitStream.Write(tamed ? m_Interaction.ability : ePetAbilityType::Invalid); // Something with the overhead icon? const bool interacting = m_Interaction.obj != LWOOBJID_EMPTY; @@ -205,12 +207,13 @@ void PetComponent::OnUse(Entity* originator) { buildFile = std::string(result.getStringField("ValidPiecesLXF")); - PuzzleData data; - data.buildFile = buildFile; - data.puzzleModelLot = result.getIntField("PuzzleModelLot"); - data.timeLimit = result.getFloatField("Timelimit"); - data.numValidPieces = result.getIntField("NumValidPieces"); - data.imaginationCost = result.getIntField("imagCostPerBuild"); + PuzzleData data{ + .puzzleModelLot = result.getIntField("PuzzleModelLot"), + .buildFile = buildFile, + .timeLimit = static_cast(result.getFloatField("Timelimit")), + .imaginationCost = result.getIntField("imagCostPerBuild"), + .numValidPieces = result.getIntField("NumValidPieces") + }; if (data.timeLimit <= 0) data.timeLimit = 60; imaginationCost = data.imaginationCost; @@ -306,7 +309,7 @@ void PetComponent::OnUse(Entity* originator) { GameMessages::SendNotifyPetTamingPuzzleSelected(originator->GetObjectID(), bricks, originator->GetSystemAddress()); m_Tamer = originator->GetObjectID(); - m_Flags.Set(PetFlag::IDLE, PetFlag::UNKNOWN4); + m_Flags.Set(); currentActivities.insert_or_assign(m_Tamer, m_Parent->GetObjectID()); @@ -334,7 +337,7 @@ void PetComponent::Update(float deltaTime) { ClientFailTamingMinigame(); // TODO: This is not despawning the built model correctly } - if (m_Flags.Has(PetFlag::SPAWNING)) OnSpawn(); + if (m_Flags.Has()) OnSpawn(); // Handle pet AI states switch (m_State) { @@ -501,7 +504,7 @@ void PetComponent::NotifyTamingBuildSuccess(const NiPoint3 position) { missionComponent->Progress(eMissionTaskType::PET_TAMING, m_Parent->GetLOT()); } - m_Flags.Reset(PetFlag::IDLE); + m_Flags.Reset(); auto* const characterComponent = tamer->GetComponent(); if (characterComponent != nullptr) { @@ -612,7 +615,7 @@ void PetComponent::ClientExitTamingMinigame(bool voluntaryExit) { currentActivities.erase(m_Tamer); - m_Flags.Reset(PetFlag::TAMEABLE); + m_Flags.Reset(); m_Tamer = LWOOBJID_EMPTY; m_Timer = 0; @@ -661,7 +664,7 @@ void PetComponent::ClientFailTamingMinigame() { currentActivities.erase(m_Tamer); - m_Flags.Reset(PetFlag::TAMEABLE); + m_Flags.Reset(); m_Tamer = LWOOBJID_EMPTY; m_Timer = 0; @@ -721,15 +724,14 @@ void PetComponent::OnSpawn() { m_Parent->SetOwnerOverride(m_Owner); m_MovementAI->SetMaxSpeed(m_PetInfo.sprintSpeed); m_MovementAI->SetHaltDistance(m_FollowRadius); - //SetOnlyFlag(IDLE); //SetStatus(PetFlag::NONE); SetPetAiState(PetAiState::follow); } else { - m_Flags.Set(PetFlag::TAMEABLE); + m_Flags.Set(); SetPetAiState(PetAiState::idle); } - m_Flags.Set(PetFlag::IDLE); - m_Flags.Unset(PetFlag::SPAWNING); + m_Flags.Set(); + m_Flags.Unset(); Game::entityManager->SerializeEntity(m_Parent); } @@ -848,7 +850,7 @@ void PetComponent::StopInteract(bool bDontSerialize) { m_Interaction.type = PetInteractType::none; m_Interaction.ability = petAbility; SetPetAiState(PetAiState::follow); - m_Flags.Reset(PetFlag::IDLE); + m_Flags.Reset(); SetIsReadyToInteract(false); SetIsHandlingInteraction(false); // Needed? m_MovementAI->SetMaxSpeed(m_PetInfo.sprintSpeed); @@ -871,8 +873,8 @@ void PetComponent::SetupInteractBouncer() { constexpr auto petAbility = ePetAbilityType::JumpOnObject; m_Interaction.ability = petAbility; - m_Flags.Unset(PetFlag::IDLE); - m_Flags.Set(PetFlag::ON_SWITCH, PetFlag::NOT_WAITING); //SetStatus(PetFlag::NOT_WAITING); // TODO: Double-check this is the right flag being set + m_Flags.Unset(); + m_Flags.Set(); // TODO: Double-check this is the right flag being set LOG_DEBUG("m_Flags = %d", m_Flags); Game::entityManager->SerializeEntity(m_Parent); // TODO: Double-check pet packet captures @@ -913,8 +915,6 @@ void PetComponent::StartInteractBouncer() { SetIsHandlingInteraction(true); SwitchComponent* closestSwitch = SwitchComponent::GetClosestSwitch(m_MovementAI->GetDestination()); // TODO: Find a better way to do this closestSwitch->EntityEnter(m_Parent); - - //m_Timer += 0.5; } void PetComponent::HandleInteractBouncer() { @@ -954,8 +954,8 @@ void PetComponent::SetupInteractTreasureDig() { constexpr auto petAbility = ePetAbilityType::DigAtPosition; m_Interaction.ability = petAbility; - m_Flags.Unset(PetFlag::IDLE); - m_Flags.Set(PetFlag::ON_SWITCH, PetFlag::NOT_WAITING); // TODO: Double-check this is the right flag being set + m_Flags.Unset(); + m_Flags.Set(); // TODO: Double-check this is the right flag being set LOG_DEBUG("m_Flags = %d", m_Flags); Game::entityManager->SerializeEntity(m_Parent); // TODO: Double-check pet packet captures @@ -989,8 +989,8 @@ void PetComponent::StartInteractTreasureDig() { Game::entityManager->SerializeEntity(user); SetIsHandlingInteraction(true); - m_Flags.Unset(PetFlag::ON_SWITCH, PetFlag::NOT_WAITING); // TODO: FIND THE CORRECT STATUS TO USE HERE - m_Flags.Set(PetFlag::IDLE); + m_Flags.Unset(); // TODO: FIND THE CORRECT STATUS TO USE HERE + m_Flags.Set(); LOG_DEBUG("StartInteractTreasureDig() m_Flags = %d", m_Flags); Game::entityManager->SerializeEntity(m_Parent); @@ -1039,7 +1039,7 @@ void PetComponent::Activate(Item* item, bool registerPet, bool fromTaming) { // auto* const owner = GetOwner(); if (!owner) return; - m_Flags.Set(PetFlag::SPAWNING); + m_Flags.Set(); auto databaseData = inventoryComponent->GetDatabasePet(m_DatabaseId); diff --git a/dGame/dComponents/PetComponent.h b/dGame/dComponents/PetComponent.h index cdde1297b..80abf0df8 100644 --- a/dGame/dComponents/PetComponent.h +++ b/dGame/dComponents/PetComponent.h @@ -379,7 +379,7 @@ class PetComponent final : public Component { /** * The time limit to complete the build */ - int32_t timeLimit; + float timeLimit; /** * The imagination cost for the tamer to start the minigame @@ -432,7 +432,7 @@ class PetComponent final : public Component { /** * Flags that indicate that a player has tamed a pet, indexed by the LOT of the pet */ - static std::map petFlags; + static const std::map petFlags; /** * The halting radius of the pet while following a player TODO: Move into struct? diff --git a/dGame/dGameMessages/GameMessages.cpp b/dGame/dGameMessages/GameMessages.cpp index 05a6af84a..571e0fa5c 100644 --- a/dGame/dGameMessages/GameMessages.cpp +++ b/dGame/dGameMessages/GameMessages.cpp @@ -523,14 +523,14 @@ void GameMessages::SendNotifyClientFlagChange(const LWOOBJID& objectID, uint32_t SEND_PACKET; } -void GameMessages::SendHelp(const LWOOBJID& objectId, const eHelpType help, const SystemAddress& sysAddr) { +void GameMessages::SendHelp(const LWOOBJID objectId, const eHelpType help, const SystemAddress& sysAddr) { CBITSTREAM; CMSGHEADER; - + bitStream.Write(objectId); bitStream.Write(eGameMessageType::HELP); bitStream.Write(help); - + SEND_PACKET; } @@ -1186,7 +1186,7 @@ void GameMessages::SendPlayerReachedRespawnCheckpoint(Entity* entity, const NiPo const bool bIsNotIdentity = rotation != NiQuaternionConstant::IDENTITY; bitStream.Write(bIsNotIdentity); - + if (bIsNotIdentity) { bitStream.Write(rotation.w); bitStream.Write(rotation.x); diff --git a/dGame/dGameMessages/GameMessages.h b/dGame/dGameMessages/GameMessages.h index c0c71b969..30019d0aa 100644 --- a/dGame/dGameMessages/GameMessages.h +++ b/dGame/dGameMessages/GameMessages.h @@ -86,7 +86,7 @@ namespace GameMessages { void SendAddItemToInventoryClientSync(Entity* entity, const SystemAddress& sysAddr, Item* item, const LWOOBJID& objectID, bool showFlyingLoot, int itemCount, LWOOBJID subKey = LWOOBJID_EMPTY, eLootSourceType lootSourceType = eLootSourceType::NONE); void SendNotifyClientFlagChange(const LWOOBJID& objectID, uint32_t iFlagID, bool bFlag, const SystemAddress& sysAddr); - void SendHelp(const LWOOBJID& objectId, const eHelpType help, const SystemAddress& sysAddr); + void SendHelp(const LWOOBJID objectId, const eHelpType help, const SystemAddress& sysAddr); void SendChangeObjectWorldState(const LWOOBJID& objectID, eObjectWorldState state, const SystemAddress& sysAddr); void SendOfferMission(const LWOOBJID& entity, const SystemAddress& sysAddr, int32_t missionID, const LWOOBJID& offererID); diff --git a/dScripts/02_server/Pets/DamagingPets.cpp b/dScripts/02_server/Pets/DamagingPets.cpp index a289e261d..ab4e30a4f 100644 --- a/dScripts/02_server/Pets/DamagingPets.cpp +++ b/dScripts/02_server/Pets/DamagingPets.cpp @@ -77,10 +77,10 @@ void DamagingPets::MakeUntamable(Entity* self) { auto* const petComponent = self->GetComponent(); // If the pet is currently not being tamed, make it hostile - if (petComponent != nullptr && !petComponent->m_Flags.Has(PetFlag::NOT_WAITING)) { + if (petComponent != nullptr && !petComponent->m_Flags.Has()) { self->SetNetworkVar(u"bIAmTamable", false); self->SetVar(u"IsEvil", true); - petComponent->m_Flags.Set(PetFlag::IDLE); + petComponent->m_Flags.Set(); auto* combatAIComponent = self->GetComponent(); if (combatAIComponent != nullptr) { @@ -110,7 +110,7 @@ void DamagingPets::ClearEffects(Entity* self) { auto* petComponent = self->GetComponent(); if (petComponent != nullptr) { - petComponent->m_Flags.Set(PetFlag::TAMEABLE, PetFlag::UNKNOWN2); + petComponent->m_Flags.Set(); } auto* combatAIComponent = self->GetComponent(); diff --git a/tests/dCommonTests/FlagTests.cpp b/tests/dCommonTests/FlagTests.cpp index 0e9f5acd0..49dca7726 100644 --- a/tests/dCommonTests/FlagTests.cpp +++ b/tests/dCommonTests/FlagTests.cpp @@ -10,9 +10,65 @@ enum class TestFlag : uint8_t { }; /** - * Test bitset flags + * Test bitset flags (compile-time methods) */ -TEST(FlagTests, FlagMethodTest) { +TEST(FlagTests, FlagMethodTestCompileTime) { + using enum TestFlag; + + auto flag = Flag{}; + + // Test setting and reading single flags, exclusively + flag.Reset(); + ASSERT_TRUE(flag.HasOnly()); + flag.Reset(); + ASSERT_TRUE(flag.HasOnly()); + ASSERT_FALSE(flag.HasOnly()); + + // Test setting and reading multiple flags, exclusively + flag.Reset(); + ASSERT_FALSE(flag.Has()); + ASSERT_TRUE(flag.Has()); + ASSERT_TRUE(flag.Has()); + ASSERT_TRUE((flag.Has())); + ASSERT_FALSE(flag.HasOnly()); + ASSERT_FALSE(flag.HasOnly()); + ASSERT_TRUE((flag.HasOnly())); + + // Test flags are being properly reset for next batch of tests + flag.Reset(); + ASSERT_TRUE(flag.Has()); + ASSERT_TRUE(flag.HasOnly()); + ASSERT_FALSE(flag.Has()); + ASSERT_FALSE((flag.Has())); + + // Test setting and reading single flags, non-exclusively + flag.Set(); + ASSERT_TRUE(flag.Has()); + ASSERT_FALSE(flag.Has()); + + // Test setting and reading multiple flags, non-exclusively + flag.Set(); + ASSERT_TRUE((flag.Has())); + ASSERT_TRUE(flag.Has()); + ASSERT_FALSE((flag.Has())); + ASSERT_TRUE((flag.Has())); + ASSERT_FALSE(flag.Has()); + ASSERT_FALSE((flag.Has())); + + // Test unsetting and reading multiple flags, non-exclusively + flag.Unset(); + ASSERT_FALSE((flag.Has())); + ASSERT_FALSE(flag.Has()); + ASSERT_TRUE((flag.Has())); + ASSERT_FALSE((flag.Has())); + ASSERT_FALSE(flag.Has()); + ASSERT_FALSE((flag.Has())); +} + +/** + * Test bitset flags (run-time methods) +*/ +TEST(FlagTests, FlagMethodTestRunTime) { using enum TestFlag; auto flag = Flag{}; @@ -64,4 +120,3 @@ TEST(FlagTests, FlagMethodTest) { ASSERT_FALSE(flag.Has(FLAG1)); ASSERT_FALSE(flag.Has(FLAG1, FLAG3, FLAG2, FLAG4)); } - From 071c022058d2796e226a5fab23b785ce70f92744 Mon Sep 17 00:00:00 2001 From: jadebenn Date: Sat, 20 Apr 2024 17:28:13 -0500 Subject: [PATCH 63/73] this commit may be kinda broke but I'm gonna push it to check something --- dGame/dComponents/PetComponent.cpp | 159 +++++++++--------- dGame/dComponents/PetComponent.h | 25 +-- .../dComponentsTests/PetComponentTests.cpp | 20 +-- 3 files changed, 102 insertions(+), 102 deletions(-) diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index 0c867e21e..ff2581596 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -85,7 +85,7 @@ PetComponent::PetComponent(Entity* parentEntity, uint32_t componentId) m_MovementAI = nullptr; m_ReadyToInteract = false; - m_State = PetAiState::spawn; + m_State = PetAiState::SPAWN; SetIsHandlingInteraction(false); std::string checkPreconditions = GeneralUtils::UTF16ToWTF8(parentEntity->GetVar(u"CheckPrecondition")); @@ -139,23 +139,76 @@ void PetComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpd void PetComponent::OnUse(Entity* originator) { LOG_DEBUG("PET USE!"); + if (!IsReadyToInteract()) return; - if (IsReadyToInteract()) { - switch (m_Interaction.ability) { - case ePetAbilityType::DigAtPosition: // Treasure dig - StartInteractTreasureDig(); - break; + switch (m_Interaction.ability) { + case ePetAbilityType::DigAtPosition: // Treasure dig + StartInteractTreasureDig(); + break; + + case ePetAbilityType::JumpOnObject: // Bouncer + StartInteractBouncer(); + break; + + default: // Pet taming minigame + StartTamingMinigame(originator); + break; + } +} + +void PetComponent::Update(float deltaTime) { + // Update timers + m_TimerBounce -= deltaTime; + + if (m_Timer > 0) { + m_Timer -= deltaTime; + return; + } + + // Remove "left behind" pets and handle failing pet taming minigame + if (m_Owner != LWOOBJID_EMPTY) { + const Entity* const owner = GetOwner(); + if (!owner) { + m_Parent->Kill(); + return; + } + } else { + ClientFailTamingMinigame(); // TODO: This is not despawning the built model correctly + } + + if (m_Flags.Has()) OnSpawn(); - case ePetAbilityType::JumpOnObject: // Bouncer - StartInteractBouncer(); - break; + // Handle pet AI states + switch (m_State) { + case PetAiState::IDLE: + Wander(); + break; - default: - break; + case PetAiState::FOLLOW: + OnFollow(deltaTime); + break; + + case PetAiState::GO_TO_OBJ: + if (m_MovementAI->AtFinalWaypoint()) { + LOG_DEBUG("Reached object!"); + m_MovementAI->Stop(); + SetPetAiState(PetAiState::INTERACT); + } else { + m_Timer += 0.5f; } + break; + + case PetAiState::INTERACT: + OnInteract(); + break; + + default: + LOG_DEBUG("Unknown state: %d!", m_Flags); + break; } +} - // The minigame logic beneath this comment should be rewritten... eventually +void PetComponent::StartTamingMinigame(Entity* originator) { if (m_Owner != LWOOBJID_EMPTY) return; if (m_Tamer != LWOOBJID_EMPTY) { @@ -167,7 +220,6 @@ void PetComponent::OnUse(Entity* originator) { } auto* const inventoryComponent = originator->GetComponent(); - if (!inventoryComponent) return; if (m_Preconditions.has_value() && !m_Preconditions->Check(originator, true)) return; @@ -186,7 +238,7 @@ void PetComponent::OnUse(Entity* originator) { std::string buildFile; // It may make sense to move this minigame-specific logic into another file - if (cached == buildCache.end()) { + if (cached == buildCache.cend()) { auto query = CDClientDatabase::CreatePreppedStmt( "SELECT ValidPiecesLXF, PuzzleModelLot, Timelimit, NumValidPieces, imagCostPerBuild FROM TamingBuildPuzzles WHERE NPCLot = ?;"); query.bind(1, static_cast(m_Parent->GetLOT())); @@ -226,11 +278,9 @@ void PetComponent::OnUse(Entity* originator) { } const auto* const destroyableComponent = originator->GetComponent(); - if (!destroyableComponent) return; const auto imagination = destroyableComponent->GetImagination(); - if (imagination < imaginationCost) return; const auto& bricks = BrickDatabase::GetBricks(buildFile); @@ -243,13 +293,11 @@ void PetComponent::OnUse(Entity* originator) { } const auto petPosition = m_Parent->GetPosition(); - const auto originatorPosition = originator->GetPosition(); m_Parent->SetRotation(NiQuaternion::LookAt(petPosition, originatorPosition)); float interactionDistance = m_Parent->GetVar(u"interaction_distance"); - if (interactionDistance <= 0) { interactionDistance = 15; } @@ -317,58 +365,6 @@ void PetComponent::OnUse(Entity* originator) { m_Parent->GetScript()->OnNotifyPetTamingMinigame(m_Parent, originator, ePetTamingNotifyType::BEGIN); } -void PetComponent::Update(float deltaTime) { - // Update timers - m_TimerBounce -= deltaTime; - - if (m_Timer > 0) { - m_Timer -= deltaTime; - return; - } - - // Remove "left behind" pets and handle failing pet taming minigame - if (m_Owner != LWOOBJID_EMPTY) { - const Entity* const owner = GetOwner(); - if (!owner) { - m_Parent->Kill(); - return; - } - } else { - ClientFailTamingMinigame(); // TODO: This is not despawning the built model correctly - } - - if (m_Flags.Has()) OnSpawn(); - - // Handle pet AI states - switch (m_State) { - case PetAiState::idle: - Wander(); - break; - - case PetAiState::follow: - OnFollow(deltaTime); - break; - - case PetAiState::goToObj: - if (m_MovementAI->AtFinalWaypoint()) { - LOG_DEBUG("Reached object!"); - m_MovementAI->Stop(); - SetPetAiState(PetAiState::interact); - } else { - m_Timer += 0.5f; - } - break; - - case PetAiState::interact: - OnInteract(); - break; - - default: - LOG_DEBUG("Unknown state: %d!", m_Flags); - break; - } -} - void PetComponent::TryBuild(uint32_t numBricks, bool clientFailed) { if (m_Tamer == LWOOBJID_EMPTY) return; @@ -628,7 +624,7 @@ void PetComponent::ClientExitTamingMinigame(bool voluntaryExit) { void PetComponent::StartTimer() { const auto& cached = buildCache.find(m_Parent->GetLOT()); - if (cached == buildCache.end()) { + if (cached == buildCache.cend()) { return; } @@ -724,10 +720,10 @@ void PetComponent::OnSpawn() { m_Parent->SetOwnerOverride(m_Owner); m_MovementAI->SetMaxSpeed(m_PetInfo.sprintSpeed); m_MovementAI->SetHaltDistance(m_FollowRadius); - SetPetAiState(PetAiState::follow); + SetPetAiState(PetAiState::FOLLOW); } else { m_Flags.Set(); - SetPetAiState(PetAiState::idle); + SetPetAiState(PetAiState::IDLE); } m_Flags.Set(); @@ -748,7 +744,7 @@ void PetComponent::OnFollow(const float deltaTime) { const LWOOBJID switchID = closestSwitch->GetParentEntity()->GetObjectID(); const float distance = Vector3::DistanceSquared(ownerPos, switchPos); if (distance < 16 * 16) { - StartInteract(switchPos, PetInteractType::bouncer, switchID); + StartInteract(switchPos, PetInteractType::BOUNCER, switchID); return; } } @@ -764,7 +760,7 @@ void PetComponent::OnFollow(const float deltaTime) { const NiPoint3 treasurePos = closestTreasure->GetPosition(); const float distance = Vector3::DistanceSquared(ownerPos, treasurePos); if (distance < 16 * 16) { - StartInteract(treasurePos, PetInteractType::treasure, m_Owner); + StartInteract(treasurePos, PetInteractType::TREASURE, m_Owner); return; } } @@ -810,12 +806,12 @@ void PetComponent::OnInteract() { } switch (m_Interaction.type) { - case PetInteractType::bouncer: + case PetInteractType::BOUNCER: if (IsReadyToInteract()) HandleInteractBouncer(); else SetupInteractBouncer(); break; - case PetInteractType::treasure: + case PetInteractType::TREASURE: if (IsReadyToInteract()) HandleInteractTreasureDig(); else SetupInteractTreasureDig(); break; @@ -832,7 +828,7 @@ void PetComponent::StartInteract(const NiPoint3 position, const PetInteractType m_Interaction.obj = interactID; // TODO: Check if this should be serialized for goToObj m_Interaction.type = interactionType; m_Interaction.ability = ePetAbilityType::GoToObject; - SetPetAiState(PetAiState::goToObj); + SetPetAiState(PetAiState::GO_TO_OBJ); m_MovementAI->SetMaxSpeed(m_PetInfo.runSpeed); m_MovementAI->SetHaltDistance(0.0f); m_MovementAI->SetDestination(position); @@ -847,11 +843,10 @@ void PetComponent::StopInteract(bool bDontSerialize) { constexpr auto petAbility = ePetAbilityType::Invalid; m_Interaction.obj = LWOOBJID_EMPTY; - m_Interaction.type = PetInteractType::none; + m_Interaction.type = PetInteractType::NONE; m_Interaction.ability = petAbility; - SetPetAiState(PetAiState::follow); + SetPetAiState(PetAiState::FOLLOW); m_Flags.Reset(); - SetIsReadyToInteract(false); SetIsHandlingInteraction(false); // Needed? m_MovementAI->SetMaxSpeed(m_PetInfo.sprintSpeed); m_MovementAI->SetHaltDistance(m_FollowRadius); @@ -1170,7 +1165,7 @@ void PetComponent::Command(const NiPoint3& position, const LWOOBJID source, cons GameMessages::SendPlayEmote(m_Parent->GetObjectID(), typeId, owner->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS); } else if (commandType == 3) { StopInteract(); // TODO: Verify this is necessary - SetPetAiState(PetAiState::follow); + SetPetAiState(PetAiState::FOLLOW); } else if (commandType == 6) { // TODO: Go to player } diff --git a/dGame/dComponents/PetComponent.h b/dGame/dComponents/PetComponent.h index 80abf0df8..8e190d69f 100644 --- a/dGame/dComponents/PetComponent.h +++ b/dGame/dComponents/PetComponent.h @@ -18,21 +18,21 @@ * The current state of the pet AI */ enum class PetAiState : uint8_t { - idle = 0, // Doing nothing - spawn, // Spawning into the world - follow, // Begin following - goToObj, // Go to object - interact, // Interact with an object - despawn // Despawning from world + IDLE = 0, // Doing nothing + SPAWN, // Spawning into the world + FOLLOW, // Begin following + GO_TO_OBJ, // Go to object + INTERACT, // Interact with an object + DESPAWN // Despawning from world }; /* * The type of object the pet is interacting with */ enum class PetInteractType : uint8_t { - none, // Not interacting - treasure, // Treasure dig - bouncer // Bouncer switch + NONE, // Not interacting + TREASURE, // Treasure dig + BOUNCER // Bouncer switch }; /** @@ -113,6 +113,11 @@ class PetComponent final : public Component { */ void OnUse(Entity* originator) override; + /** + * Start the pet taming minigame + */ + void StartTamingMinigame(Entity* originator); + /** * Attempts to complete the pet minigame by passing a list of bricks to build the minigame model. * @param bricks the bricks to try to complete the minigame with @@ -396,7 +401,7 @@ class PetComponent final : public Component { /** * The type of object that the pet is currently interacting with (e.g. a treasure chest or switch) */ - PetInteractType type = PetInteractType::none; + PetInteractType type = PetInteractType::NONE; /** * The interaction ability diff --git a/tests/dGameTests/dComponentsTests/PetComponentTests.cpp b/tests/dGameTests/dComponentsTests/PetComponentTests.cpp index 31633303f..90c2607e9 100644 --- a/tests/dGameTests/dComponentsTests/PetComponentTests.cpp +++ b/tests/dGameTests/dComponentsTests/PetComponentTests.cpp @@ -41,25 +41,25 @@ TEST_F(PetTest, PlacementNewAddComponentTest) { // Test getting initial status ASSERT_EQ(petComponent->GetParent()->GetObjectID(), 15); - ASSERT_EQ(petComponent->GetPetAiState(), PetAiState::spawn); + ASSERT_EQ(petComponent->GetPetAiState(), PetAiState::SPAWN); } TEST_F(PetTest, PetAiState) { const auto initialState = petComponent->GetPetAiState(); - ASSERT_EQ(initialState, PetAiState::spawn); + ASSERT_EQ(initialState, PetAiState::SPAWN); - petComponent->SetPetAiState(PetAiState::follow); - ASSERT_EQ(PetAiState::follow, petComponent->GetPetAiState()); + petComponent->SetPetAiState(PetAiState::FOLLOW); + ASSERT_EQ(PetAiState::FOLLOW, petComponent->GetPetAiState()); - petComponent->SetPetAiState(PetAiState::idle); - ASSERT_EQ(PetAiState::idle, petComponent->GetPetAiState()); + petComponent->SetPetAiState(PetAiState::IDLE); + ASSERT_EQ(PetAiState::IDLE, petComponent->GetPetAiState()); - petComponent->SetPetAiState(PetAiState::interact); - ASSERT_EQ(PetAiState::interact, petComponent->GetPetAiState()); + petComponent->SetPetAiState(PetAiState::INTERACT); + ASSERT_EQ(PetAiState::INTERACT, petComponent->GetPetAiState()); - petComponent->SetPetAiState(PetAiState::despawn); - ASSERT_EQ(PetAiState::despawn, petComponent->GetPetAiState()); + petComponent->SetPetAiState(PetAiState::DESPAWN); + ASSERT_EQ(PetAiState::DESPAWN, petComponent->GetPetAiState()); } // Test the pet use logic From 7b223d1cc2ac0fa418e13ad0bdab0d4490ccdf56 Mon Sep 17 00:00:00 2001 From: jadebenn Date: Sat, 20 Apr 2024 17:32:19 -0500 Subject: [PATCH 64/73] Revert "this commit may be kinda broke but I'm gonna push it to check something" This reverts commit 071c022058d2796e226a5fab23b785ce70f92744. --- dGame/dComponents/PetComponent.cpp | 159 +++++++++--------- dGame/dComponents/PetComponent.h | 25 ++- .../dComponentsTests/PetComponentTests.cpp | 20 +-- 3 files changed, 102 insertions(+), 102 deletions(-) diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index ff2581596..0c867e21e 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -85,7 +85,7 @@ PetComponent::PetComponent(Entity* parentEntity, uint32_t componentId) m_MovementAI = nullptr; m_ReadyToInteract = false; - m_State = PetAiState::SPAWN; + m_State = PetAiState::spawn; SetIsHandlingInteraction(false); std::string checkPreconditions = GeneralUtils::UTF16ToWTF8(parentEntity->GetVar(u"CheckPrecondition")); @@ -139,76 +139,23 @@ void PetComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpd void PetComponent::OnUse(Entity* originator) { LOG_DEBUG("PET USE!"); - if (!IsReadyToInteract()) return; - switch (m_Interaction.ability) { - case ePetAbilityType::DigAtPosition: // Treasure dig - StartInteractTreasureDig(); - break; - - case ePetAbilityType::JumpOnObject: // Bouncer - StartInteractBouncer(); - break; - - default: // Pet taming minigame - StartTamingMinigame(originator); - break; - } -} - -void PetComponent::Update(float deltaTime) { - // Update timers - m_TimerBounce -= deltaTime; - - if (m_Timer > 0) { - m_Timer -= deltaTime; - return; - } - - // Remove "left behind" pets and handle failing pet taming minigame - if (m_Owner != LWOOBJID_EMPTY) { - const Entity* const owner = GetOwner(); - if (!owner) { - m_Parent->Kill(); - return; - } - } else { - ClientFailTamingMinigame(); // TODO: This is not despawning the built model correctly - } - - if (m_Flags.Has()) OnSpawn(); + if (IsReadyToInteract()) { + switch (m_Interaction.ability) { + case ePetAbilityType::DigAtPosition: // Treasure dig + StartInteractTreasureDig(); + break; - // Handle pet AI states - switch (m_State) { - case PetAiState::IDLE: - Wander(); - break; + case ePetAbilityType::JumpOnObject: // Bouncer + StartInteractBouncer(); + break; - case PetAiState::FOLLOW: - OnFollow(deltaTime); - break; - - case PetAiState::GO_TO_OBJ: - if (m_MovementAI->AtFinalWaypoint()) { - LOG_DEBUG("Reached object!"); - m_MovementAI->Stop(); - SetPetAiState(PetAiState::INTERACT); - } else { - m_Timer += 0.5f; + default: + break; } - break; - - case PetAiState::INTERACT: - OnInteract(); - break; - - default: - LOG_DEBUG("Unknown state: %d!", m_Flags); - break; } -} -void PetComponent::StartTamingMinigame(Entity* originator) { + // The minigame logic beneath this comment should be rewritten... eventually if (m_Owner != LWOOBJID_EMPTY) return; if (m_Tamer != LWOOBJID_EMPTY) { @@ -220,6 +167,7 @@ void PetComponent::StartTamingMinigame(Entity* originator) { } auto* const inventoryComponent = originator->GetComponent(); + if (!inventoryComponent) return; if (m_Preconditions.has_value() && !m_Preconditions->Check(originator, true)) return; @@ -238,7 +186,7 @@ void PetComponent::StartTamingMinigame(Entity* originator) { std::string buildFile; // It may make sense to move this minigame-specific logic into another file - if (cached == buildCache.cend()) { + if (cached == buildCache.end()) { auto query = CDClientDatabase::CreatePreppedStmt( "SELECT ValidPiecesLXF, PuzzleModelLot, Timelimit, NumValidPieces, imagCostPerBuild FROM TamingBuildPuzzles WHERE NPCLot = ?;"); query.bind(1, static_cast(m_Parent->GetLOT())); @@ -278,9 +226,11 @@ void PetComponent::StartTamingMinigame(Entity* originator) { } const auto* const destroyableComponent = originator->GetComponent(); + if (!destroyableComponent) return; const auto imagination = destroyableComponent->GetImagination(); + if (imagination < imaginationCost) return; const auto& bricks = BrickDatabase::GetBricks(buildFile); @@ -293,11 +243,13 @@ void PetComponent::StartTamingMinigame(Entity* originator) { } const auto petPosition = m_Parent->GetPosition(); + const auto originatorPosition = originator->GetPosition(); m_Parent->SetRotation(NiQuaternion::LookAt(petPosition, originatorPosition)); float interactionDistance = m_Parent->GetVar(u"interaction_distance"); + if (interactionDistance <= 0) { interactionDistance = 15; } @@ -365,6 +317,58 @@ void PetComponent::StartTamingMinigame(Entity* originator) { m_Parent->GetScript()->OnNotifyPetTamingMinigame(m_Parent, originator, ePetTamingNotifyType::BEGIN); } +void PetComponent::Update(float deltaTime) { + // Update timers + m_TimerBounce -= deltaTime; + + if (m_Timer > 0) { + m_Timer -= deltaTime; + return; + } + + // Remove "left behind" pets and handle failing pet taming minigame + if (m_Owner != LWOOBJID_EMPTY) { + const Entity* const owner = GetOwner(); + if (!owner) { + m_Parent->Kill(); + return; + } + } else { + ClientFailTamingMinigame(); // TODO: This is not despawning the built model correctly + } + + if (m_Flags.Has()) OnSpawn(); + + // Handle pet AI states + switch (m_State) { + case PetAiState::idle: + Wander(); + break; + + case PetAiState::follow: + OnFollow(deltaTime); + break; + + case PetAiState::goToObj: + if (m_MovementAI->AtFinalWaypoint()) { + LOG_DEBUG("Reached object!"); + m_MovementAI->Stop(); + SetPetAiState(PetAiState::interact); + } else { + m_Timer += 0.5f; + } + break; + + case PetAiState::interact: + OnInteract(); + break; + + default: + LOG_DEBUG("Unknown state: %d!", m_Flags); + break; + } +} + void PetComponent::TryBuild(uint32_t numBricks, bool clientFailed) { if (m_Tamer == LWOOBJID_EMPTY) return; @@ -624,7 +628,7 @@ void PetComponent::ClientExitTamingMinigame(bool voluntaryExit) { void PetComponent::StartTimer() { const auto& cached = buildCache.find(m_Parent->GetLOT()); - if (cached == buildCache.cend()) { + if (cached == buildCache.end()) { return; } @@ -720,10 +724,10 @@ void PetComponent::OnSpawn() { m_Parent->SetOwnerOverride(m_Owner); m_MovementAI->SetMaxSpeed(m_PetInfo.sprintSpeed); m_MovementAI->SetHaltDistance(m_FollowRadius); - SetPetAiState(PetAiState::FOLLOW); + SetPetAiState(PetAiState::follow); } else { m_Flags.Set(); - SetPetAiState(PetAiState::IDLE); + SetPetAiState(PetAiState::idle); } m_Flags.Set(); @@ -744,7 +748,7 @@ void PetComponent::OnFollow(const float deltaTime) { const LWOOBJID switchID = closestSwitch->GetParentEntity()->GetObjectID(); const float distance = Vector3::DistanceSquared(ownerPos, switchPos); if (distance < 16 * 16) { - StartInteract(switchPos, PetInteractType::BOUNCER, switchID); + StartInteract(switchPos, PetInteractType::bouncer, switchID); return; } } @@ -760,7 +764,7 @@ void PetComponent::OnFollow(const float deltaTime) { const NiPoint3 treasurePos = closestTreasure->GetPosition(); const float distance = Vector3::DistanceSquared(ownerPos, treasurePos); if (distance < 16 * 16) { - StartInteract(treasurePos, PetInteractType::TREASURE, m_Owner); + StartInteract(treasurePos, PetInteractType::treasure, m_Owner); return; } } @@ -806,12 +810,12 @@ void PetComponent::OnInteract() { } switch (m_Interaction.type) { - case PetInteractType::BOUNCER: + case PetInteractType::bouncer: if (IsReadyToInteract()) HandleInteractBouncer(); else SetupInteractBouncer(); break; - case PetInteractType::TREASURE: + case PetInteractType::treasure: if (IsReadyToInteract()) HandleInteractTreasureDig(); else SetupInteractTreasureDig(); break; @@ -828,7 +832,7 @@ void PetComponent::StartInteract(const NiPoint3 position, const PetInteractType m_Interaction.obj = interactID; // TODO: Check if this should be serialized for goToObj m_Interaction.type = interactionType; m_Interaction.ability = ePetAbilityType::GoToObject; - SetPetAiState(PetAiState::GO_TO_OBJ); + SetPetAiState(PetAiState::goToObj); m_MovementAI->SetMaxSpeed(m_PetInfo.runSpeed); m_MovementAI->SetHaltDistance(0.0f); m_MovementAI->SetDestination(position); @@ -843,10 +847,11 @@ void PetComponent::StopInteract(bool bDontSerialize) { constexpr auto petAbility = ePetAbilityType::Invalid; m_Interaction.obj = LWOOBJID_EMPTY; - m_Interaction.type = PetInteractType::NONE; + m_Interaction.type = PetInteractType::none; m_Interaction.ability = petAbility; - SetPetAiState(PetAiState::FOLLOW); + SetPetAiState(PetAiState::follow); m_Flags.Reset(); + SetIsReadyToInteract(false); SetIsHandlingInteraction(false); // Needed? m_MovementAI->SetMaxSpeed(m_PetInfo.sprintSpeed); m_MovementAI->SetHaltDistance(m_FollowRadius); @@ -1165,7 +1170,7 @@ void PetComponent::Command(const NiPoint3& position, const LWOOBJID source, cons GameMessages::SendPlayEmote(m_Parent->GetObjectID(), typeId, owner->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS); } else if (commandType == 3) { StopInteract(); // TODO: Verify this is necessary - SetPetAiState(PetAiState::FOLLOW); + SetPetAiState(PetAiState::follow); } else if (commandType == 6) { // TODO: Go to player } diff --git a/dGame/dComponents/PetComponent.h b/dGame/dComponents/PetComponent.h index 8e190d69f..80abf0df8 100644 --- a/dGame/dComponents/PetComponent.h +++ b/dGame/dComponents/PetComponent.h @@ -18,21 +18,21 @@ * The current state of the pet AI */ enum class PetAiState : uint8_t { - IDLE = 0, // Doing nothing - SPAWN, // Spawning into the world - FOLLOW, // Begin following - GO_TO_OBJ, // Go to object - INTERACT, // Interact with an object - DESPAWN // Despawning from world + idle = 0, // Doing nothing + spawn, // Spawning into the world + follow, // Begin following + goToObj, // Go to object + interact, // Interact with an object + despawn // Despawning from world }; /* * The type of object the pet is interacting with */ enum class PetInteractType : uint8_t { - NONE, // Not interacting - TREASURE, // Treasure dig - BOUNCER // Bouncer switch + none, // Not interacting + treasure, // Treasure dig + bouncer // Bouncer switch }; /** @@ -113,11 +113,6 @@ class PetComponent final : public Component { */ void OnUse(Entity* originator) override; - /** - * Start the pet taming minigame - */ - void StartTamingMinigame(Entity* originator); - /** * Attempts to complete the pet minigame by passing a list of bricks to build the minigame model. * @param bricks the bricks to try to complete the minigame with @@ -401,7 +396,7 @@ class PetComponent final : public Component { /** * The type of object that the pet is currently interacting with (e.g. a treasure chest or switch) */ - PetInteractType type = PetInteractType::NONE; + PetInteractType type = PetInteractType::none; /** * The interaction ability diff --git a/tests/dGameTests/dComponentsTests/PetComponentTests.cpp b/tests/dGameTests/dComponentsTests/PetComponentTests.cpp index 90c2607e9..31633303f 100644 --- a/tests/dGameTests/dComponentsTests/PetComponentTests.cpp +++ b/tests/dGameTests/dComponentsTests/PetComponentTests.cpp @@ -41,25 +41,25 @@ TEST_F(PetTest, PlacementNewAddComponentTest) { // Test getting initial status ASSERT_EQ(petComponent->GetParent()->GetObjectID(), 15); - ASSERT_EQ(petComponent->GetPetAiState(), PetAiState::SPAWN); + ASSERT_EQ(petComponent->GetPetAiState(), PetAiState::spawn); } TEST_F(PetTest, PetAiState) { const auto initialState = petComponent->GetPetAiState(); - ASSERT_EQ(initialState, PetAiState::SPAWN); + ASSERT_EQ(initialState, PetAiState::spawn); - petComponent->SetPetAiState(PetAiState::FOLLOW); - ASSERT_EQ(PetAiState::FOLLOW, petComponent->GetPetAiState()); + petComponent->SetPetAiState(PetAiState::follow); + ASSERT_EQ(PetAiState::follow, petComponent->GetPetAiState()); - petComponent->SetPetAiState(PetAiState::IDLE); - ASSERT_EQ(PetAiState::IDLE, petComponent->GetPetAiState()); + petComponent->SetPetAiState(PetAiState::idle); + ASSERT_EQ(PetAiState::idle, petComponent->GetPetAiState()); - petComponent->SetPetAiState(PetAiState::INTERACT); - ASSERT_EQ(PetAiState::INTERACT, petComponent->GetPetAiState()); + petComponent->SetPetAiState(PetAiState::interact); + ASSERT_EQ(PetAiState::interact, petComponent->GetPetAiState()); - petComponent->SetPetAiState(PetAiState::DESPAWN); - ASSERT_EQ(PetAiState::DESPAWN, petComponent->GetPetAiState()); + petComponent->SetPetAiState(PetAiState::despawn); + ASSERT_EQ(PetAiState::despawn, petComponent->GetPetAiState()); } // Test the pet use logic From 68bb51f408b43be0ce370a3bf752a58acf5a7f77 Mon Sep 17 00:00:00 2001 From: jadebenn Date: Sat, 20 Apr 2024 17:44:18 -0500 Subject: [PATCH 65/73] move the pet taming minigame start logic into a separate function --- dGame/dComponents/PetComponent.cpp | 109 +++++++++++++++-------------- dGame/dComponents/PetComponent.h | 5 ++ 2 files changed, 60 insertions(+), 54 deletions(-) diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index 0c867e21e..8781f9bae 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -149,12 +149,65 @@ void PetComponent::OnUse(Entity* originator) { case ePetAbilityType::JumpOnObject: // Bouncer StartInteractBouncer(); break; + } + } else { + StartTamingMinigame(originator); + } +} - default: - break; +void PetComponent::Update(float deltaTime) { + // Update timers + m_TimerBounce -= deltaTime; + + if (m_Timer > 0) { + m_Timer -= deltaTime; + return; + } + + // Remove "left behind" pets and handle failing pet taming minigame + if (m_Owner != LWOOBJID_EMPTY) { + const Entity* const owner = GetOwner(); + if (!owner) { + m_Parent->Kill(); + return; } + } else { + ClientFailTamingMinigame(); // TODO: This is not despawning the built model correctly + } + + if (m_Flags.Has()) OnSpawn(); + + // Handle pet AI states + switch (m_State) { + case PetAiState::idle: + Wander(); + break; + + case PetAiState::follow: + OnFollow(deltaTime); + break; + + case PetAiState::goToObj: + if (m_MovementAI->AtFinalWaypoint()) { + LOG_DEBUG("Reached object!"); + m_MovementAI->Stop(); + SetPetAiState(PetAiState::interact); + } else { + m_Timer += 0.5f; + } + break; + + case PetAiState::interact: + OnInteract(); + break; + + default: + LOG_DEBUG("Unknown state: %d!", m_Flags); + break; } +} +void PetComponent::StartTamingMinigame(Entity* originator) { // The minigame logic beneath this comment should be rewritten... eventually if (m_Owner != LWOOBJID_EMPTY) return; @@ -317,58 +370,6 @@ void PetComponent::OnUse(Entity* originator) { m_Parent->GetScript()->OnNotifyPetTamingMinigame(m_Parent, originator, ePetTamingNotifyType::BEGIN); } -void PetComponent::Update(float deltaTime) { - // Update timers - m_TimerBounce -= deltaTime; - - if (m_Timer > 0) { - m_Timer -= deltaTime; - return; - } - - // Remove "left behind" pets and handle failing pet taming minigame - if (m_Owner != LWOOBJID_EMPTY) { - const Entity* const owner = GetOwner(); - if (!owner) { - m_Parent->Kill(); - return; - } - } else { - ClientFailTamingMinigame(); // TODO: This is not despawning the built model correctly - } - - if (m_Flags.Has()) OnSpawn(); - - // Handle pet AI states - switch (m_State) { - case PetAiState::idle: - Wander(); - break; - - case PetAiState::follow: - OnFollow(deltaTime); - break; - - case PetAiState::goToObj: - if (m_MovementAI->AtFinalWaypoint()) { - LOG_DEBUG("Reached object!"); - m_MovementAI->Stop(); - SetPetAiState(PetAiState::interact); - } else { - m_Timer += 0.5f; - } - break; - - case PetAiState::interact: - OnInteract(); - break; - - default: - LOG_DEBUG("Unknown state: %d!", m_Flags); - break; - } -} - void PetComponent::TryBuild(uint32_t numBricks, bool clientFailed) { if (m_Tamer == LWOOBJID_EMPTY) return; diff --git a/dGame/dComponents/PetComponent.h b/dGame/dComponents/PetComponent.h index 80abf0df8..5b9fe38b8 100644 --- a/dGame/dComponents/PetComponent.h +++ b/dGame/dComponents/PetComponent.h @@ -120,6 +120,11 @@ class PetComponent final : public Component { */ void TryBuild(uint32_t numBricks, bool clientFailed); + /** + * Start the pet taming minigame + */ + void StartTamingMinigame(Entity* originator); + /** * Handles a notification from the client regarding the completion of the pet minigame, adding the pet to their * inventory. From 514732d395e6212f5b0818a6094bc47272d6e7b4 Mon Sep 17 00:00:00 2001 From: jadebenn Date: Sat, 14 Dec 2024 18:25:37 -0600 Subject: [PATCH 66/73] formatting & const --- dGame/dComponents/PetComponent.cpp | 77 +++++++----------------------- 1 file changed, 16 insertions(+), 61 deletions(-) diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index 0296e74a5..0e18800b5 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -215,14 +215,11 @@ void PetComponent::StartTamingMinigame(Entity* originator) { if (m_Tamer != LWOOBJID_EMPTY) { const auto* const tamer = Game::entityManager->GetEntity(m_Tamer); - if (tamer != nullptr) return; - m_Tamer = LWOOBJID_EMPTY; } auto* const inventoryComponent = originator->GetComponent(); - if (!inventoryComponent) return; if (m_Preconditions.has_value() && !m_Preconditions->Check(originator, true)) { @@ -261,7 +258,6 @@ void PetComponent::StartTamingMinigame(Entity* originator) { } const auto petPosition = m_Parent->GetPosition(); - const auto originatorPosition = originator->GetPosition(); m_Parent->SetRotation(NiQuaternion::LookAt(petPosition, originatorPosition)); @@ -272,22 +268,17 @@ void PetComponent::StartTamingMinigame(Entity* originator) { } auto position = originatorPosition; - - NiPoint3 forward = NiQuaternion::LookAt(m_Parent->GetPosition(), originator->GetPosition()).GetForwardVector(); + auto forward = NiQuaternion::LookAt(m_Parent->GetPosition(), originator->GetPosition()).GetForwardVector(); forward.y = 0; if (dpWorld::IsLoaded()) { - NiPoint3 attempt = petPosition + forward * interactionDistance; - - NiPoint3 nearestPoint = dpWorld::GetNavMesh()->NearestPoint(attempt); + auto attempt = petPosition + forward * interactionDistance; + auto nearestPoint = dpWorld::GetNavMesh()->NearestPoint(attempt); while (std::abs(nearestPoint.y - petPosition.y) > 4 && interactionDistance > 10) { - const NiPoint3 forward = m_Parent->GetRotation().GetForwardVector(); - + const auto forward = m_Parent->GetRotation().GetForwardVector(); attempt = originatorPosition + forward * interactionDistance; - nearestPoint = dpWorld::GetNavMesh()->NearestPoint(attempt); - interactionDistance -= 0.5f; } @@ -296,7 +287,6 @@ void PetComponent::StartTamingMinigame(Entity* originator) { position = petPosition + forward * interactionDistance; } - const auto rotation = NiQuaternion::LookAt(position, petPosition); GameMessages::SendNotifyPetTamingMinigame( @@ -344,26 +334,19 @@ void PetComponent::TryBuild(uint32_t numBricks, bool clientFailed) { if (m_Tamer == LWOOBJID_EMPTY) return; auto* const tamer = Game::entityManager->GetEntity(m_Tamer); - if (!tamer) { m_Tamer = LWOOBJID_EMPTY; - return; } - const auto* const entry = CDClientManager::GetTable()->GetByLOT(m_Parent->GetLOT()); - if (!entry) return; - auto* const destroyableComponent = tamer->GetComponent(); - if (!destroyableComponent) return; - auto imagination = destroyableComponent->GetImagination(); - - imagination -= entry->imaginationCost; + const auto* const entry = CDClientManager::GetTable()->GetByLOT(m_Parent->GetLOT()); + if (!entry) return; + const auto imagination = destroyableComponent->GetImagination() - entry->imaginationCost; destroyableComponent->SetImagination(imagination); - Game::entityManager->SerializeEntity(tamer); if (clientFailed) { @@ -383,10 +366,8 @@ void PetComponent::NotifyTamingBuildSuccess(const NiPoint3 position) { if (m_Tamer == LWOOBJID_EMPTY) return; auto* const tamer = Game::entityManager->GetEntity(m_Tamer); - if (!tamer) { m_Tamer = LWOOBJID_EMPTY; - return; } @@ -396,20 +377,17 @@ void PetComponent::NotifyTamingBuildSuccess(const NiPoint3 position) { GameMessages::SendPlayFXEffect(tamer, -1, u"petceleb", "", LWOOBJID_EMPTY, 1, 1, true); RenderComponent::PlayAnimation(tamer, u"rebuild-celebrate"); - EntityInfo info{}; + auto info = EntityInfo{}; info.lot = entry->puzzleModelLot; info.pos = position; info.rot = NiQuaternionConstant::IDENTITY; info.spawnerID = tamer->GetObjectID(); auto* const modelEntity = Game::entityManager->CreateEntity(info); - m_ModelId = modelEntity->GetObjectID(); - Game::entityManager->ConstructEntity(modelEntity); GameMessages::SendNotifyTamingModelLoadedOnServer(m_Tamer, tamer->GetSystemAddress()); - GameMessages::SendPetResponse(m_Tamer, m_Parent->GetObjectID(), 0, 10, 0, tamer->GetSystemAddress()); auto* const inventoryComponent = tamer->GetComponent(); @@ -426,9 +404,7 @@ void PetComponent::NotifyTamingBuildSuccess(const NiPoint3 position) { petName += "'s Pet"; GameMessages::SendAddPetToPlayer(m_Tamer, 0, GeneralUtils::UTF8ToUTF16(petName), petSubKey, m_Parent->GetLOT(), tamer->GetSystemAddress()); - GameMessages::SendRegisterPetID(m_Tamer, m_Parent->GetObjectID(), tamer->GetSystemAddress()); - GameMessages::SendRegisterPetDBID(m_Tamer, petSubKey, tamer->GetSystemAddress()); inventoryComponent->AddItem(m_Parent->GetLOT(), 1, eLootSourceType::ACTIVITY, eInventoryType::MODELS, {}, LWOOBJID_EMPTY, true, false, petSubKey); @@ -466,7 +442,6 @@ void PetComponent::NotifyTamingBuildSuccess(const NiPoint3 position) { } auto* const missionComponent = tamer->GetComponent(); - if (missionComponent != nullptr) { missionComponent->Progress(eMissionTaskType::PET_TAMING, m_Parent->GetLOT()); } @@ -497,17 +472,15 @@ void PetComponent::RequestSetPetName(const std::u16string& name) { return; } - auto* tamer = Game::entityManager->GetEntity(m_Tamer); - + auto* const tamer = Game::entityManager->GetEntity(m_Tamer); if (tamer == nullptr) { m_Tamer = LWOOBJID_EMPTY; - return; } LOG("Got set pet name (%s)", GeneralUtils::UTF16ToWTF8(name).c_str()); - auto* inventoryComponent = tamer->GetComponent(); + auto* const inventoryComponent = tamer->GetComponent(); if (!inventoryComponent) return; m_ModerationStatus = 1; // Pending @@ -518,8 +491,8 @@ void PetComponent::RequestSetPetName(const std::u16string& name) { Game::entityManager->SerializeEntity(m_Parent); - std::u16string u16name = GeneralUtils::UTF8ToUTF16(m_Name); - std::u16string u16ownerName = GeneralUtils::UTF8ToUTF16(m_OwnerName); + const auto u16name = GeneralUtils::UTF8ToUTF16(m_Name); + const auto u16ownerName = GeneralUtils::UTF8ToUTF16(m_OwnerName); GameMessages::SendSetPetName(m_Tamer, u16name, m_DatabaseId, tamer->GetSystemAddress()); GameMessages::SendSetPetName(m_Tamer, u16name, LWOOBJID_EMPTY, tamer->GetSystemAddress()); GameMessages::SendPetNameChanged(m_Parent->GetObjectID(), m_ModerationStatus, u16name, u16ownerName, UNASSIGNED_SYSTEM_ADDRESS); @@ -537,7 +510,7 @@ void PetComponent::RequestSetPetName(const std::u16string& name) { UNASSIGNED_SYSTEM_ADDRESS ); - auto* characterComponent = tamer->GetComponent(); + auto* const characterComponent = tamer->GetComponent(); if (characterComponent != nullptr) { characterComponent->SetCurrentActivity(eGameActivity::NONE); Game::entityManager->SerializeEntity(tamer); @@ -545,13 +518,11 @@ void PetComponent::RequestSetPetName(const std::u16string& name) { GameMessages::SendTerminateInteraction(m_Tamer, eTerminateType::FROM_INTERACTION, m_Parent->GetObjectID()); auto* const modelEntity = Game::entityManager->GetEntity(m_ModelId); - if (modelEntity != nullptr) { modelEntity->Smash(m_Tamer); } currentActivities.erase(m_Tamer); - m_Tamer = LWOOBJID_EMPTY; // Notify the end of a pet taming minigame @@ -562,10 +533,8 @@ void PetComponent::ClientExitTamingMinigame(bool voluntaryExit) { if (m_Tamer == LWOOBJID_EMPTY) return; auto* const tamer = Game::entityManager->GetEntity(m_Tamer); - if (!tamer) { m_Tamer = LWOOBJID_EMPTY; - return; } @@ -587,7 +556,6 @@ void PetComponent::ClientExitTamingMinigame(bool voluntaryExit) { Game::entityManager->SerializeEntity(tamer); } GameMessages::SendNotifyTamingModelLoadedOnServer(m_Tamer, tamer->GetSystemAddress()); - GameMessages::SendTerminateInteraction(m_Tamer, eTerminateType::FROM_INTERACTION, m_Parent->GetObjectID()); currentActivities.erase(m_Tamer); @@ -613,10 +581,8 @@ void PetComponent::ClientFailTamingMinigame() { if (m_Tamer == LWOOBJID_EMPTY) return; auto* const tamer = Game::entityManager->GetEntity(m_Tamer); - if (!tamer) { m_Tamer = LWOOBJID_EMPTY; - return; } @@ -632,13 +598,12 @@ void PetComponent::ClientFailTamingMinigame() { UNASSIGNED_SYSTEM_ADDRESS ); - auto* characterComponent = tamer->GetComponent(); + auto* const characterComponent = tamer->GetComponent(); if (characterComponent != nullptr) { characterComponent->SetCurrentActivity(eGameActivity::NONE); Game::entityManager->SerializeEntity(tamer); } GameMessages::SendNotifyTamingModelLoadedOnServer(m_Tamer, tamer->GetSystemAddress()); - GameMessages::SendTerminateInteraction(m_Tamer, eTerminateType::FROM_INTERACTION, m_Parent->GetObjectID()); currentActivities.erase(m_Tamer); @@ -721,7 +686,7 @@ void PetComponent::OnFollow(const float deltaTime) { const NiPoint3 ownerPos = owner->GetPosition(); // Find interactions - SwitchComponent* closestSwitch = SwitchComponent::GetClosestSwitch(ownerPos); + const auto* const closestSwitch = SwitchComponent::GetClosestSwitch(ownerPos); if (closestSwitch != nullptr && !closestSwitch->GetActive()) { const NiPoint3 switchPos = closestSwitch->GetParentEntity()->GetPosition(); const LWOOBJID switchID = closestSwitch->GetParentEntity()->GetObjectID(); @@ -775,7 +740,7 @@ void PetComponent::OnFollow(const float deltaTime) { } void PetComponent::OnInteract() { - Entity* owner = GetOwner(); + Entity* const owner = GetOwner(); if (!owner) return; const NiPoint3 ownerPos = owner->GetPosition(); @@ -1109,37 +1074,27 @@ void PetComponent::Deactivate(const eHelpType msg) { } GameMessages::SendPlayFXEffect(m_Parent->GetObjectID(), -1, u"despawn", "", LWOOBJID_EMPTY, 1, 1, true); - activePets.erase(m_Owner); - m_Parent->Kill(); const auto* const owner = GetOwner(); - if (!owner) return; GameMessages::SendMarkInventoryItemAsActive(m_Owner, false, eUnequippableActiveType::PET, m_ItemId, owner->GetSystemAddress()); - GameMessages::SendAddPetToPlayer(m_Owner, 0, u"", LWOOBJID_EMPTY, LOT_NULL, owner->GetSystemAddress()); - GameMessages::SendRegisterPetID(m_Owner, LWOOBJID_EMPTY, owner->GetSystemAddress()); - GameMessages::SendRegisterPetDBID(m_Owner, LWOOBJID_EMPTY, owner->GetSystemAddress()); - GameMessages::SendShowPetActionButton(m_Owner, ePetAbilityType::Invalid, false, owner->GetSystemAddress()); } void PetComponent::Release() { auto* const inventoryComponent = GetOwner()->GetComponent(); - if (!inventoryComponent) return; Deactivate(); - inventoryComponent->RemoveDatabasePet(m_DatabaseId); auto* const item = inventoryComponent->FindItemBySubKey(m_DatabaseId); - item->SetCount(0, false, false); } From 43681b44949a4fffb3515245c587bd5e99c4718d Mon Sep 17 00:00:00 2001 From: jadebenn Date: Sat, 14 Dec 2024 19:03:37 -0600 Subject: [PATCH 67/73] more formatting & const --- dGame/dGameMessages/GameMessages.cpp | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/dGame/dGameMessages/GameMessages.cpp b/dGame/dGameMessages/GameMessages.cpp index 75a2d454f..803b0dbe0 100644 --- a/dGame/dGameMessages/GameMessages.cpp +++ b/dGame/dGameMessages/GameMessages.cpp @@ -3723,21 +3723,17 @@ void GameMessages::HandlePetTamingTryBuild(RakNet::BitStream& inStream, Entity* bool clientFailed; inStream.Read(brickCount); - bricks.reserve(brickCount); for (uint32_t i = 0; i < brickCount; i++) { Brick brick; - inStream.Read(brick); - bricks.push_back(brick); } clientFailed = inStream.ReadBit(); - auto* petComponent = PetComponent::GetTamingPet(entity->GetObjectID()); - + auto* const petComponent = PetComponent::GetTamingPet(entity->GetObjectID()); if (petComponent == nullptr) { return; } @@ -3747,11 +3743,9 @@ void GameMessages::HandlePetTamingTryBuild(RakNet::BitStream& inStream, Entity* void GameMessages::HandleNotifyTamingBuildSuccess(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) { NiPoint3 position; - inStream.Read(position); - auto* petComponent = PetComponent::GetTamingPet(entity->GetObjectID()); - + auto* const petComponent = PetComponent::GetTamingPet(entity->GetObjectID()); if (petComponent == nullptr) { return; } From 96f224c38c6aead561ffc19df1e815ec7fb1ea4d Mon Sep 17 00:00:00 2001 From: jadebenn Date: Sat, 14 Dec 2024 19:29:22 -0600 Subject: [PATCH 68/73] move semantics --- dGame/dComponents/InventoryComponent.cpp | 15 ++++++++------- dGame/dComponents/InventoryComponent.h | 2 +- dGame/dComponents/PetComponent.cpp | 21 +++++++++------------ 3 files changed, 18 insertions(+), 20 deletions(-) diff --git a/dGame/dComponents/InventoryComponent.cpp b/dGame/dComponents/InventoryComponent.cpp index 366051ab6..f108e13c7 100644 --- a/dGame/dComponents/InventoryComponent.cpp +++ b/dGame/dComponents/InventoryComponent.cpp @@ -1253,8 +1253,8 @@ void InventoryComponent::SpawnPet(Item* item) { Game::entityManager->ConstructEntity(pet); } -void InventoryComponent::SetDatabasePet(LWOOBJID id, const DatabasePet& data) { - m_Pets.insert_or_assign(id, data); +void InventoryComponent::SetDatabasePet(LWOOBJID id, DatabasePet&& data) { + m_Pets.insert_or_assign(id, std::move(data)); } const DatabasePet& InventoryComponent::GetDatabasePet(LWOOBJID id) const { @@ -1580,12 +1580,13 @@ void InventoryComponent::LoadPetXml(const tinyxml2::XMLDocument& document) { petElement->QueryAttribute("m", &moderationStatus); const char* name = petElement->Attribute("n"); - DatabasePet databasePet; - databasePet.lot = lot; - databasePet.moderationState = moderationStatus; - databasePet.name = std::string(name); + auto databasePet = DatabasePet{ + .lot = lot, + .name = std::string(name), + .moderationState = moderationStatus, + }; - SetDatabasePet(id, databasePet); + SetDatabasePet(id, std::move(databasePet)); petElement = petElement->NextSiblingElement(); } diff --git a/dGame/dComponents/InventoryComponent.h b/dGame/dComponents/InventoryComponent.h index 0055fcea7..46ddf7750 100644 --- a/dGame/dComponents/InventoryComponent.h +++ b/dGame/dComponents/InventoryComponent.h @@ -339,7 +339,7 @@ class InventoryComponent final : public Component { * @param id the id of the pet to find * @param data the data to store on the pet */ - void SetDatabasePet(LWOOBJID id, const DatabasePet& data); + void SetDatabasePet(LWOOBJID id, DatabasePet&& data); /** * Returns the database pet information for an object diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index 0e18800b5..8aab9393c 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -393,15 +393,12 @@ void PetComponent::NotifyTamingBuildSuccess(const NiPoint3 position) { auto* const inventoryComponent = tamer->GetComponent(); if (!inventoryComponent) return; - LWOOBJID petSubKey = ObjectIDManager::GenerateRandomObjectID(); - + auto petSubKey = ObjectIDManager::GenerateRandomObjectID(); GeneralUtils::SetBit(petSubKey, eObjectBits::CHARACTER); GeneralUtils::SetBit(petSubKey, eObjectBits::PERSISTENT); - m_DatabaseId = petSubKey; - std::string petName = tamer->GetCharacter()->GetName(); - petName += "'s Pet"; + auto petName = tamer->GetCharacter()->GetName() + "'s Pet"; GameMessages::SendAddPetToPlayer(m_Tamer, 0, GeneralUtils::UTF8ToUTF16(petName), petSubKey, m_Parent->GetLOT(), tamer->GetSystemAddress()); GameMessages::SendRegisterPetID(m_Tamer, m_Parent->GetObjectID(), tamer->GetSystemAddress()); @@ -412,13 +409,13 @@ void PetComponent::NotifyTamingBuildSuccess(const NiPoint3 position) { auto* const item = inventoryComponent->FindItemBySubKey(petSubKey, MODELS); if (!item) return; - DatabasePet databasePet{}; - - databasePet.lot = m_Parent->GetLOT(); - databasePet.moderationState = 1; - databasePet.name = petName; + auto databasePet = DatabasePet{ + .lot = m_Parent->GetLOT(), + .name = std::move(petName), + .moderationState = 1, + }; - inventoryComponent->SetDatabasePet(petSubKey, databasePet); + inventoryComponent->SetDatabasePet(petSubKey, std::move(databasePet)); Activate(item, false, true); @@ -1000,7 +997,7 @@ void PetComponent::Activate(Item* item, bool registerPet, bool fromTaming) { // databaseData.name = m_Name; databaseData.moderationState = m_ModerationStatus; - inventoryComponent->SetDatabasePet(m_DatabaseId, databaseData); + inventoryComponent->SetDatabasePet(m_DatabaseId, std::move(databaseData)); updatedModerationStatus = true; } else { From 0786986345c500c550f1dd6514565f6f0121e22e Mon Sep 17 00:00:00 2001 From: jadebenn Date: Sat, 14 Dec 2024 19:52:03 -0600 Subject: [PATCH 69/73] partially address comments --- dCommon/CMakeLists.txt | 1 - dCommon/Flag.h | 1 + dGame/dComponents/PetComponent.h | 2 +- dGame/dComponents/SwitchComponent.cpp | 2 -- dGame/dGameMessages/GameMessages.h | 3 +-- 5 files changed, 3 insertions(+), 6 deletions(-) diff --git a/dCommon/CMakeLists.txt b/dCommon/CMakeLists.txt index 7bb52a67d..d020ff72f 100644 --- a/dCommon/CMakeLists.txt +++ b/dCommon/CMakeLists.txt @@ -4,7 +4,6 @@ set(DCOMMON_SOURCES "BinaryIO.cpp" "dConfig.cpp" "Diagnostics.cpp" - "Flag.h" "Logger.cpp" "Game.cpp" "GeneralUtils.cpp" diff --git a/dCommon/Flag.h b/dCommon/Flag.h index d76725d03..7353d9c9c 100644 --- a/dCommon/Flag.h +++ b/dCommon/Flag.h @@ -1,3 +1,4 @@ +#pragma once #include "GeneralUtils.h" /** diff --git a/dGame/dComponents/PetComponent.h b/dGame/dComponents/PetComponent.h index fd709b894..70773e943 100644 --- a/dGame/dComponents/PetComponent.h +++ b/dGame/dComponents/PetComponent.h @@ -6,7 +6,7 @@ #include "MovementAIComponent.h" #include "Component.h" #include "Preconditions.h" -#include "ePetAbilityType.h" +#include "eHelpType.h" #include "eReplicaComponentType.h" #include "ePetAbilityType.h" #include "CDPetComponentTable.h" diff --git a/dGame/dComponents/SwitchComponent.cpp b/dGame/dComponents/SwitchComponent.cpp index 028e485f0..184df9433 100644 --- a/dGame/dComponents/SwitchComponent.cpp +++ b/dGame/dComponents/SwitchComponent.cpp @@ -79,8 +79,6 @@ void SwitchComponent::EntityEnter(Entity* entity) { if (m_PetBouncer != nullptr) { GameMessages::SendPlayFXEffect(m_Parent->GetObjectID(), 2602, u"pettriggeractive", "BounceEffect", LWOOBJID_EMPTY, 1, 1, true); - //RenderComponent::PlayAnimation(m_Parent, u"engaged"); - //m_PetBouncer->SetPetBouncerEnabled(true); } else { Game::entityManager->SerializeEntity(m_Parent); } diff --git a/dGame/dGameMessages/GameMessages.h b/dGame/dGameMessages/GameMessages.h index 312d16614..831869132 100644 --- a/dGame/dGameMessages/GameMessages.h +++ b/dGame/dGameMessages/GameMessages.h @@ -10,8 +10,6 @@ #include "eEndBehavior.h" #include "eCyclingMode.h" #include "eLootSourceType.h" -#include "eHelpType.h" -#include "ePetAbilityType.h" #include "Brick.h" class AMFBaseValue; @@ -28,6 +26,7 @@ enum class eAnimationFlags : uint32_t; enum class eUnequippableActiveType; enum eInventoryType : uint32_t; enum class eGameMasterLevel : uint8_t; +enum class eHelpType : int32_t; enum class eMatchUpdate : int32_t; enum class eKillType : uint32_t; enum class eObjectWorldState : uint32_t; From 472f873e57593a49e85b6a1b2c5803e8a80d566b Mon Sep 17 00:00:00 2001 From: jadebenn Date: Sat, 14 Dec 2024 20:03:16 -0600 Subject: [PATCH 70/73] further address feedback --- dGame/dComponents/PetComponent.cpp | 2 +- dGame/dGameMessages/GameMessages.cpp | 2 +- dGame/dGameMessages/GameMessages.h | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index 8aab9393c..1c9728ef1 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -855,7 +855,7 @@ void PetComponent::StartInteractBouncer() { // THIS IS ALL BAD, BAD, BAD! FIX IT, ME! >:( SetIsHandlingInteraction(true); SwitchComponent* closestSwitch = SwitchComponent::GetClosestSwitch(m_MovementAI->GetDestination()); // TODO: Find a better way to do this - closestSwitch->EntityEnter(m_Parent); + closestSwitch->OnUse(m_Parent); } void PetComponent::HandleInteractBouncer() { diff --git a/dGame/dGameMessages/GameMessages.cpp b/dGame/dGameMessages/GameMessages.cpp index 803b0dbe0..9e1a004da 100644 --- a/dGame/dGameMessages/GameMessages.cpp +++ b/dGame/dGameMessages/GameMessages.cpp @@ -3606,7 +3606,7 @@ void GameMessages::SendBouncerActiveStatus(LWOOBJID objectId, bool bActive, cons SEND_PACKET; } -void GameMessages::SendRequestClientBounce(const LWOOBJID& objectId, const LWOOBJID& bounceTargetId, const NiPoint3& bounceTargetPos, const NiPoint3& bouncedObjLinVel, const LWOOBJID& requestSourceId, const bool bAllBounced, const bool bAllowClientOverload, const SystemAddress& sysAddr) { +void GameMessages::SendRequestClientBounce(const LWOOBJID objectId, const LWOOBJID bounceTargetId, const NiPoint3& bounceTargetPos, const NiPoint3& bouncedObjLinVel, const LWOOBJID& requestSourceId, const bool bAllBounced, const bool bAllowClientOverload, const SystemAddress& sysAddr) { CBITSTREAM; CMSGHEADER; diff --git a/dGame/dGameMessages/GameMessages.h b/dGame/dGameMessages/GameMessages.h index 831869132..be1fe145e 100644 --- a/dGame/dGameMessages/GameMessages.h +++ b/dGame/dGameMessages/GameMessages.h @@ -412,7 +412,7 @@ namespace GameMessages { void SendBouncerActiveStatus(LWOOBJID objectId, bool bActive, const SystemAddress& sysAddr); /** - * Sends a request to the client to bounce (I think?) + * Sends a request to the client to bounce * @param objectId Object ID * @param bounceTargetId The object ID of the bounce target * @param bounceTargetPos The position of the bounce target @@ -421,7 +421,7 @@ namespace GameMessages { * @param bAllBounced Whether to bounce all entities standing on the bouncer pad * @param bAllowClientOverload TODO: UNUSED */ - void SendRequestClientBounce(const LWOOBJID& objectId, const LWOOBJID& bounceTargetId, const NiPoint3& bounceTargetPos, const NiPoint3& bouncedObjLinVel, const LWOOBJID& requestSourceId, const bool bAllBounced, const bool bAllowClientOverload, const SystemAddress& sysAddr); + void SendRequestClientBounce(LWOOBJID objectId, LWOOBJID bounceTargetId, const NiPoint3& bounceTargetPos, const NiPoint3& bouncedObjLinVel, const LWOOBJID& requestSourceId, const bool bAllBounced, const bool bAllowClientOverload, const SystemAddress& sysAddr); void SendSetPetName(LWOOBJID objectId, std::u16string name, LWOOBJID petDBID, const SystemAddress& sysAddr); From f7bcbb0506db12a761a05c152c7acafe16e9111e Mon Sep 17 00:00:00 2001 From: jadebenn Date: Sat, 14 Dec 2024 20:03:56 -0600 Subject: [PATCH 71/73] no longer bad (you know it) --- dGame/dComponents/PetComponent.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index 1c9728ef1..7fbce903f 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -852,7 +852,6 @@ void PetComponent::StartInteractBouncer() { destroyableComponent->SetImagination(imagination); Game::entityManager->SerializeEntity(user); - // THIS IS ALL BAD, BAD, BAD! FIX IT, ME! >:( SetIsHandlingInteraction(true); SwitchComponent* closestSwitch = SwitchComponent::GetClosestSwitch(m_MovementAI->GetDestination()); // TODO: Find a better way to do this closestSwitch->OnUse(m_Parent); From d42351d1f3025831208d842cdfd11916e1006a65 Mon Sep 17 00:00:00 2001 From: jadebenn Date: Sat, 14 Dec 2024 20:24:53 -0600 Subject: [PATCH 72/73] formatting nits --- .../02_server/Map/General/PetDigServer.cpp | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/dScripts/02_server/Map/General/PetDigServer.cpp b/dScripts/02_server/Map/General/PetDigServer.cpp index 676b4db70..2f4e89b1d 100644 --- a/dScripts/02_server/Map/General/PetDigServer.cpp +++ b/dScripts/02_server/Map/General/PetDigServer.cpp @@ -71,7 +71,7 @@ void PetDigServer::OnDie(Entity* self, Entity* killer) { treasures.erase(iterator); } - auto* owner = killer->GetOwner(); + auto* const owner = killer->GetOwner(); const auto digInfoIterator = digInfoMap.find(self->GetLOT()); const auto digInfo = digInfoIterator != digInfoMap.end() ? digInfoIterator->second : defaultDigInfo; @@ -80,7 +80,7 @@ void PetDigServer::OnDie(Entity* self, Entity* killer) { } else if (digInfo.builderOnly) { // Some treasures may only be retrieved by the player that built the diggable - auto builder = self->GetVar(u"builder"); // Set by the pet dig build script + const auto builder = self->GetVar(u"builder"); // Set by the pet dig build script if (builder != owner->GetObjectID()) return; } else if (digInfo.xBuild) { @@ -96,7 +96,7 @@ void PetDigServer::OnDie(Entity* self, Entity* killer) { // TODO: Reset other pets // Handles smashing leftovers (edge case for the AG X) - auto* xObject = Game::entityManager->GetEntity(self->GetVar(u"X")); + auto* const xObject = Game::entityManager->GetEntity(self->GetVar(u"X")); if (xObject != nullptr) { xObject->Smash(xObject->GetObjectID(), eKillType::VIOLENT); } @@ -105,24 +105,24 @@ void PetDigServer::OnDie(Entity* self, Entity* killer) { void PetDigServer::OnUse(Entity* self, Entity* user) { LOG_DEBUG("Treasure used! LWOOBJID: %d", self->GetObjectID()); - auto* petComponent = PetComponent::GetActivePet(user->GetObjectID()); + auto* const petComponent = PetComponent::GetActivePet(user->GetObjectID()); if (!petComponent) return; - if(petComponent->IsReadyToInteract()) { + if (petComponent->IsReadyToInteract()) { petComponent->StartInteractTreasureDig(); } } void PetDigServer::HandleXBuildDig(const Entity* self, Entity* owner, Entity* pet) { - auto playerID = self->GetVar(u"builder"); + const auto playerID = self->GetVar(u"builder"); if (playerID == LWOOBJID_EMPTY || playerID != owner->GetObjectID()) return; - auto* playerEntity = Game::entityManager->GetEntity(playerID); + auto* const playerEntity = Game::entityManager->GetEntity(playerID); if (!playerEntity || !playerEntity->GetCharacter()) return; - auto* player = playerEntity->GetCharacter(); + auto* const player = playerEntity->GetCharacter(); const auto groupID = self->GetVar(u"groupID"); int32_t playerFlag = 0; @@ -137,7 +137,7 @@ void PetDigServer::HandleXBuildDig(const Entity* self, Entity* owner, Entity* pe // If the player doesn't have the flag yet if (playerFlag != 0 && !player->GetPlayerFlag(playerFlag)) { - auto* petComponent = pet->GetComponent(); + auto* const petComponent = pet->GetComponent(); if (petComponent != nullptr) { // TODO: Pet state = 9 ?? } @@ -146,22 +146,22 @@ void PetDigServer::HandleXBuildDig(const Entity* self, Entity* owner, Entity* pe player->SetPlayerFlag(playerFlag, true); } - auto* xObject = Game::entityManager->GetEntity(self->GetVar(u"X")); + auto* const xObject = Game::entityManager->GetEntity(self->GetVar(u"X")); if (xObject != nullptr) { xObject->Smash(xObject->GetObjectID(), eKillType::VIOLENT); } } void PetDigServer::HandleBouncerDig(const Entity* self, const Entity* owner) { - auto bounceNumber = GeneralUtils::UTF16ToWTF8(self->GetVar(u"BouncerNumber")); - auto bouncerSpawners = Game::zoneManager->GetSpawnersByName("PetBouncer" + bounceNumber); - auto switchSpawners = Game::zoneManager->GetSpawnersByName("PetBouncerSwitch" + bounceNumber); + const auto bounceNumber = GeneralUtils::UTF16ToWTF8(self->GetVar(u"BouncerNumber")); + const auto bouncerSpawners = Game::zoneManager->GetSpawnersByName("PetBouncer" + bounceNumber); + const auto switchSpawners = Game::zoneManager->GetSpawnersByName("PetBouncerSwitch" + bounceNumber); - for (auto* bouncerSpawner : bouncerSpawners) { + for (auto* const bouncerSpawner : bouncerSpawners) { bouncerSpawner->Activate(); } - for (auto* switchSpawner : switchSpawners) { + for (auto* const switchSpawner : switchSpawners) { switchSpawner->Activate(); } } From 79c26d93301f1f0656c4d186aeabf14cdb94b8e4 Mon Sep 17 00:00:00 2001 From: jadebenn Date: Mon, 16 Dec 2024 20:36:03 -0600 Subject: [PATCH 73/73] misc --- dGame/dComponents/PetComponent.cpp | 10 +++++++++- dGame/dGameMessages/GameMessageHandler.cpp | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index 7fbce903f..f74a9aa8e 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -151,6 +151,14 @@ void PetComponent::OnUse(Entity* originator) { case ePetAbilityType::JumpOnObject: // Bouncer StartInteractBouncer(); break; + + default: + LOG_DEBUG( + "Unhandled pet interaction - Owner: %ld, ePetAbilityType %d", + m_Owner, + GeneralUtils::ToUnderlying(m_Interaction.ability) + ); + break; } } else { StartTamingMinigame(originator); @@ -323,7 +331,7 @@ void PetComponent::StartTamingMinigame(Entity* originator) { // Notify the start of a pet taming minigame m_Parent->GetScript()->OnNotifyPetTamingMinigame(m_Parent, originator, ePetTamingNotifyType::BEGIN); - auto* characterComponent = originator->GetComponent(); + auto* const characterComponent = originator->GetComponent(); if (characterComponent != nullptr) { characterComponent->SetCurrentActivity(eGameActivity::PET_TAMING); Game::entityManager->SerializeEntity(originator); diff --git a/dGame/dGameMessages/GameMessageHandler.cpp b/dGame/dGameMessages/GameMessageHandler.cpp index baa3a84e5..15d99de25 100644 --- a/dGame/dGameMessages/GameMessageHandler.cpp +++ b/dGame/dGameMessages/GameMessageHandler.cpp @@ -110,7 +110,7 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream& inStream, const System if (var) { entity->SetVar(u"dlu_first_time_load", false); InventoryComponent* inventoryComponent = entity->GetComponent(); - + if (inventoryComponent) inventoryComponent->FixInvisibleItems(); } break;