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_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<std::u16string>(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<int>(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<MovementAIComponent>();
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<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();
NiPoint3 position = m_MovementAI->GetParent()->GetPosition();
@ -387,8 +524,7 @@ void PetComponent::Update(float deltaTime) {
float haltDistance = 5;
if (closestSwitch != nullptr) {
if (!closestSwitch->GetActive()) {
if (closestSwitch != nullptr && !closestSwitch->GetActive()) {
NiPoint3 switchPosition = closestSwitch->GetParentEntity()->GetPosition();
float distance = Vector3::DistanceSquared(position, switchPosition);
if (distance < 3 * 3) {
@ -396,11 +532,9 @@ void PetComponent::Update(float deltaTime) {
closestSwitch->EntityEnter(m_Parent);
} else if (distance < 20 * 20) {
haltDistance = 1;
destination = switchPosition;
}
}
}
// Determine if the "Lost Tags" mission has been completed and digging has been unlocked
auto* missionComponent = owner->GetComponent<MissionComponent>();
@ -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?
}
}

View File

@ -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 @@ public:
~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 @@ private:
*/
uint32_t m_Status;
/**
* The current state of the pet AI
*/
PetAiState m_State;
/**
* A currently active ability, mostly unused
*/
@ -399,11 +431,6 @@ private:
*/
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 @@ private:
* 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;
};