redone pet update loop

This commit is contained in:
jadebenn 2023-12-10 19:55:36 -06:00
parent 1c01219ae9
commit 200d679dd8
2 changed files with 226 additions and 48 deletions

View File

@ -82,32 +82,41 @@ PetComponent::PetComponent(Entity* parent, uint32_t componentId): Component(pare
m_DatabaseId = LWOOBJID_EMPTY; m_DatabaseId = LWOOBJID_EMPTY;
m_Status = PetStatus::TAMEABLE; // Tameable m_Status = PetStatus::TAMEABLE; // Tameable
m_Ability = PetAbilityType::Invalid; m_Ability = PetAbilityType::Invalid;
m_StartPosition = NiPoint3::ZERO; m_StartPosition = m_Parent->GetPosition(); //NiPoint3::ZERO;
m_MovementAI = nullptr; m_MovementAI = nullptr;
m_TresureTime = 0; m_TresureTime = 0;
m_Preconditions = nullptr; m_Preconditions = nullptr;
m_ReadyToDig = false; m_ReadyToDig = false;
m_InInteract = false; SetPetAiState(PetAiState::spawn);
std::string checkPreconditions = GeneralUtils::UTF16ToWTF8(parent->GetVar<std::u16string>(u"CheckPrecondition")); std::string checkPreconditions = GeneralUtils::UTF16ToWTF8(parent->GetVar<std::u16string>(u"CheckPrecondition"));
if (!checkPreconditions.empty()) { if (!checkPreconditions.empty()) {
SetPreconditions(checkPreconditions); 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<int>(componentId)); query.bind(1, static_cast<int>(componentId));
auto result = query.execQuery(); auto result = query.execQuery();
// Should a result not exist for this pet default to 60 seconds. if (!result.eof()) {
if (!result.eof() && !result.fieldIsNull(0)) { if (!result.fieldIsNull(0))
imaginationDrainRate = result.getFloatField(0, 60.0f); m_walkSpeed = result.getFloatField(0);
} else {
imaginationDrainRate = 60.0f; 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(); 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) { void PetComponent::OnUse(Entity* originator) {
LOG("PET USE!"); LOG("PET USE!");
/*if(m_ReadyToDig) { if (m_Owner != LWOOBJID_EMPTY) return;
LOG("Dig initiated!");
m_TresureTime = 2.0f;
//m_ReadyToDig = false;
SetAbility(PetAbilityType::DigAtPosition);
}*/
if (m_Owner != LWOOBJID_EMPTY) {
return;
}
if (m_Tamer != LWOOBJID_EMPTY) { if (m_Tamer != LWOOBJID_EMPTY) {
auto* tamer = Game::entityManager->GetEntity(m_Tamer); auto* tamer = Game::entityManager->GetEntity(m_Tamer);
@ -336,29 +343,159 @@ void PetComponent::OnUse(Entity* originator) {
} }
void PetComponent::Update(float deltaTime) { void PetComponent::Update(float deltaTime) {
if (m_StartPosition == NiPoint3::ZERO) { // If pet does not have an owner, use the UpdateUnowned() loop
m_StartPosition = m_Parent->GetPosition();
}
if (m_Owner == LWOOBJID_EMPTY) { if (m_Owner == LWOOBJID_EMPTY) {
UpdateUnowned(deltaTime); UpdateUnowned(deltaTime);
return; return;
} }
// Determine pet owner
auto* owner = GetOwner(); auto* owner = GetOwner();
if (!owner) { if (!owner) {
m_Parent->Kill(); m_Parent->Kill(); // Kill pet if no owner
return; return;
} }
m_MovementAI = m_Parent->GetComponent<MovementAIComponent>(); // Update timer
if (!m_MovementAI) return; if (m_Timer > 0) {
m_Timer -= deltaTime;
return;
}
// Handle treasure timer
if (m_TresureTime > 0.0f) { //TODO: Find better trigger? if (m_TresureTime > 0.0f) { //TODO: Find better trigger?
InteractDig(deltaTime); InteractDig(deltaTime);
return; 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<MovementAIComponent>();
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<MissionComponent>();
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<MovementAIComponent>();
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<SwitchComponent>();
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<MovementAIComponent>();
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(); auto destination = owner->GetPosition();
NiPoint3 position = m_MovementAI->GetParent()->GetPosition(); NiPoint3 position = m_MovementAI->GetParent()->GetPosition();
@ -387,8 +524,7 @@ void PetComponent::Update(float deltaTime) {
float haltDistance = 5; float haltDistance = 5;
if (closestSwitch != nullptr) { if (closestSwitch != nullptr && !closestSwitch->GetActive()) {
if (!closestSwitch->GetActive()) {
NiPoint3 switchPosition = closestSwitch->GetParentEntity()->GetPosition(); NiPoint3 switchPosition = closestSwitch->GetParentEntity()->GetPosition();
float distance = Vector3::DistanceSquared(position, switchPosition); float distance = Vector3::DistanceSquared(position, switchPosition);
if (distance < 3 * 3) { if (distance < 3 * 3) {
@ -396,11 +532,9 @@ void PetComponent::Update(float deltaTime) {
closestSwitch->EntityEnter(m_Parent); closestSwitch->EntityEnter(m_Parent);
} else if (distance < 20 * 20) { } else if (distance < 20 * 20) {
haltDistance = 1; haltDistance = 1;
destination = switchPosition; destination = switchPosition;
} }
} }
}
// Determine if the "Lost Tags" mission has been completed and digging has been unlocked // Determine if the "Lost Tags" mission has been completed and digging has been unlocked
auto* missionComponent = owner->GetComponent<MissionComponent>(); auto* missionComponent = owner->GetComponent<MissionComponent>();
@ -434,13 +568,13 @@ void PetComponent::Update(float deltaTime) {
skipTresure: skipTresure:
//m_MovementAI->SetHaltDistance(haltDistance); m_MovementAI->SetHaltDistance(haltDistance);
//m_MovementAI->SetMaxSpeed(2.5f); //m_MovementAI->SetMaxSpeed(2.5f);
m_MovementAI->SetDestination(destination); m_MovementAI->SetDestination(destination);
m_Timer = 1; m_Timer = 1;*/
} }
void PetComponent::UpdateUnowned(float deltaTime) { void PetComponent::UpdateUnowned(float deltaTime) {
@ -473,6 +607,7 @@ void PetComponent::SetIsReadyToDig(bool isReady) {
//SetAbility(PetAbilityType::JumpOnObject); //SetAbility(PetAbilityType::JumpOnObject);
SetStatus(PetStatus::IS_NOT_WAITING); // Treasure dig status SetStatus(PetStatus::IS_NOT_WAITING); // Treasure dig status
m_ReadyToDig = true; m_ReadyToDig = true;
Game::entityManager->SerializeEntity(m_Parent);
} }
else { else {
LOG("Dig state ended!"); LOG("Dig state ended!");
@ -480,6 +615,7 @@ void PetComponent::SetIsReadyToDig(bool isReady) {
//SetAbility(PetAbilityType::Invalid); //SetAbility(PetAbilityType::Invalid);
SetStatus(0); // TODO: Check status SetStatus(0); // TODO: Check status
m_ReadyToDig = false; m_ReadyToDig = false;
Game::entityManager->SerializeEntity(m_Parent);
} }
} }
@ -1022,7 +1158,7 @@ void PetComponent::Command(NiPoint3 position, LWOOBJID source, int32_t commandTy
// Emotes // Emotes
GameMessages::SendPlayEmote(m_Parent->GetObjectID(), typeId, owner->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS); GameMessages::SendPlayEmote(m_Parent->GetObjectID(), typeId, owner->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS);
} else if (commandType == 3) { } else if (commandType == 3) {
// Follow me, ??? SetPetAiState(PetAiState::follow);
} else if (commandType == 6) { } else if (commandType == 6) {
// TODO: Go to player // TODO: Go to player
} }
@ -1034,7 +1170,7 @@ void PetComponent::Command(NiPoint3 position, LWOOBJID source, int32_t commandTy
// Add movement functionality // Add movement functionality
if (position != NiPoint3::ZERO) { if (position != NiPoint3::ZERO) {
m_MovementAI->SetDestination(position); 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?
} }
} }

View File

@ -6,6 +6,21 @@
#include "Preconditions.h" #include "Preconditions.h"
#include "eReplicaComponentType.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 { enum PetStatus : uint32_t {
NONE, NONE,
BEING_TAMED = 0x10, BEING_TAMED = 0x10,
@ -41,6 +56,18 @@ public:
~PetComponent() override; ~PetComponent() override;
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) 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; void Update(float deltaTime) override;
/** /**
@ -373,6 +400,11 @@ private:
*/ */
uint32_t m_Status; uint32_t m_Status;
/**
* The current state of the pet AI
*/
PetAiState m_State;
/** /**
* A currently active ability, mostly unused * A currently active ability, mostly unused
*/ */
@ -399,11 +431,6 @@ private:
*/ */
bool m_ReadyToDig; bool m_ReadyToDig;
/**
* Boolean that sets if a pet is in an interaction
*/
bool m_InInteract;
/** /**
* The position that this pet was spawned at * The position that this pet was spawned at
*/ */
@ -423,4 +450,19 @@ private:
* The rate at which imagination is drained from the user for having the pet out. * The rate at which imagination is drained from the user for having the pet out.
*/ */
float imaginationDrainRate; 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;
}; };