From 5b738dfc588d6be2e24e0065390c2b4a7f2a7fa0 Mon Sep 17 00:00:00 2001 From: jadebenn Date: Mon, 11 Dec 2023 21:10:29 -0600 Subject: [PATCH] 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 1961e6d6..b5b50b27 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 faaad97b..d74da1d5 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; + else { + m_Timer += 0.5f; } - // Handle interact state - case PetAiState::interact: { - LOG_DEBUG("Interacting with object!"); + break; - // Get movement AI component - m_MovementAI = m_Parent->GetComponent(); - if (!m_MovementAI) return; + case PetAiState::interact: + OnInteract(); + break; - // 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; - } + default: + break; } - return; - /* auto destination = owner->GetPosition(); @@ -577,7 +468,7 @@ skipTresure: 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 d3b21f94..58bc6386 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 @@ public: */ 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 @@ public: 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 @@ private: */ 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 @@ private: */ 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 */