Re-implemented treasure dig handling

This commit is contained in:
jadebenn 2023-12-13 00:14:53 -06:00
parent 5b738dfc58
commit a99f7a7fc1
3 changed files with 144 additions and 124 deletions

View File

@ -80,17 +80,17 @@ PetComponent::PetComponent(Entity* parent, uint32_t componentId): Component(pare
m_ModelId = LWOOBJID_EMPTY; m_ModelId = LWOOBJID_EMPTY;
m_Timer = 0; m_Timer = 0;
m_TimerAway = 0; m_TimerAway = 0;
m_TimerBounce = 0;
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 = m_Parent->GetPosition(); //NiPoint3::ZERO; m_StartPosition = m_Parent->GetPosition(); //NiPoint3::ZERO;
m_MovementAI = nullptr; m_MovementAI = nullptr;
m_TresureTime = 0;
m_Preconditions = nullptr; m_Preconditions = nullptr;
m_ReadyToDig = false; m_ReadyToInteract = false;
SetPetAiState(PetAiState::spawn); SetPetAiState(PetAiState::spawn);
m_FollowRadius = 5.0f; //m_FollowRadius = 8.0f;
std::string checkPreconditions = GeneralUtils::UTF16ToWTF8(parent->GetVar<std::u16string>(u"CheckPrecondition")); std::string checkPreconditions = GeneralUtils::UTF16ToWTF8(parent->GetVar<std::u16string>(u"CheckPrecondition"));
@ -160,7 +160,6 @@ void PetComponent::SetPetAiState(PetAiState newState) {
if (newState == GetPetAiState()) return; if (newState == GetPetAiState()) return;
this->m_State = newState; this->m_State = newState;
LOG_DEBUG("Set pet AI state!"); 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) {
@ -341,16 +340,24 @@ void PetComponent::Update(float deltaTime) {
return; return;
}*/ }*/
if (m_StartPosition == NiPoint3::ZERO) { // Update timers
m_StartPosition = m_Parent->GetPosition(); m_TimerBounce -= deltaTime;
}
// Update timer
if (m_Timer > 0) { if (m_Timer > 0) {
m_Timer -= deltaTime; m_Timer -= deltaTime;
return; 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) { switch (m_State) {
case PetAiState::spawn: case PetAiState::spawn:
LOG_DEBUG("Pet spawn beginning!"); LOG_DEBUG("Pet spawn beginning!");
@ -468,7 +475,7 @@ skipTresure:
m_Timer = 1;*/ 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_Tamer != LWOOBJID_EMPTY) {
if (m_Timer > 0) { if (m_Timer > 0) {
m_Timer -= deltaTime; 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) { if (isReady) {
LOG("Dig state reached!"); LOG("Dig state reached!");
//m_Interaction = closestTresure->GetObjectID(); //m_Interaction = closestTresure->GetObjectID();
@ -508,33 +515,7 @@ void PetComponent::SetIsReadyToDig(bool isReady) {
m_ReadyToDig = false; m_ReadyToDig = false;
Game::entityManager->SerializeEntity(m_Parent); 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) { void PetComponent::TryBuild(uint32_t numBricks, bool clientFailed) {
if (m_Tamer == LWOOBJID_EMPTY) return; if (m_Tamer == LWOOBJID_EMPTY) return;
@ -856,9 +837,9 @@ void PetComponent::ClientFailTamingMinigame() {
} }
void PetComponent::Wander() { void PetComponent::Wander() {
m_MovementAI = m_Parent->GetComponent<MovementAIComponent>(); //m_MovementAI = m_Parent->GetComponent<MovementAIComponent>();
if (m_MovementAI == nullptr || !m_MovementAI->AtFinalWaypoint()) return; if (/*m_MovementAI == nullptr ||*/ !m_MovementAI->AtFinalWaypoint()) return;
m_MovementAI->SetHaltDistance(0); m_MovementAI->SetHaltDistance(0);
@ -889,48 +870,48 @@ void PetComponent::Wander() {
return; return;
} }
m_MovementAI->SetMaxSpeed(info.wanderSpeed); m_MovementAI->SetMaxSpeed(m_SprintSpeed); //info.wanderSpeed);
m_MovementAI->SetDestination(destination); 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() { void PetComponent::OnSpawn() {
m_MovementAI = m_Parent->GetComponent<MovementAIComponent>(); m_MovementAI = m_Parent->GetComponent<MovementAIComponent>();
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."); LOG_DEBUG("Pet spawn complete, setting AI state.");
if (m_Owner != LWOOBJID_EMPTY) SetPetAiState(PetAiState::follow); if (m_Owner != LWOOBJID_EMPTY) {
else SetPetAiState(PetAiState::idle); 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() { void PetComponent::OnFollow() {
m_MovementAI->SetHaltDistance(5.0f); // TODO: Remove this magic number
Entity* owner = GetOwner(); Entity* owner = GetOwner();
NiPoint3 ownerPos = owner->GetPosition(); if (!owner) return;
NiPoint3 currentPos = m_MovementAI->GetParent()->GetPosition(); const NiPoint3 ownerPos = owner->GetPosition();
// If the player's position is within range, stop moving // Find interactions
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); SwitchComponent* closestSwitch = SwitchComponent::GetClosestSwitch(ownerPos);
if (closestSwitch != nullptr && !closestSwitch->GetActive()) { 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); const float distance = Vector3::DistanceSquared(ownerPos, switchPos);
if (distance < 20 * 20) { if (distance < 16 * 16) {
StartInteract(switchPos, PetInteractType::bouncer); StartInteract(switchPos, PetInteractType::bouncer, switchID);
return; return;
} }
} }
@ -942,22 +923,36 @@ void PetComponent::OnFollow() {
Entity* closestTreasure = PetDigServer::GetClosestTresure(ownerPos); Entity* closestTreasure = PetDigServer::GetClosestTresure(ownerPos);
if (closestTreasure != nullptr && digUnlocked) { 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); const float distance = Vector3::DistanceSquared(ownerPos, treasurePos);
if (distance < 10 * 10) { if (distance < 16 * 16) {
StartInteract(treasurePos, PetInteractType::treasure); StartInteract(treasurePos, PetInteractType::treasure, treasureID);
return; 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; m_Timer += 0.5f;
} }
void PetComponent::OnInteract() { void PetComponent::OnInteract() {
LOG_DEBUG("Beginning interaction with object!"); Entity* owner = GetOwner();
if (!owner) return;
NiPoint3 ownerPos = GetOwner()->GetPosition(); const NiPoint3 ownerPos = owner->GetPosition();
NiPoint3 currentPos = m_MovementAI->GetParent()->GetPosition(); const NiPoint3 currentPos = m_MovementAI->GetParent()->GetPosition();
const float distanceFromOwner = Vector3::DistanceSquared(ownerPos, currentPos); const float distanceFromOwner = Vector3::DistanceSquared(ownerPos, currentPos);
if (distanceFromOwner > 25 * 25) { if (distanceFromOwner > 25 * 25) {
@ -968,11 +963,13 @@ void PetComponent::OnInteract() {
switch (GetInteractType()) { switch (GetInteractType()) {
case PetInteractType::bouncer: case PetInteractType::bouncer:
StartInteractBouncer(); if (GetIsReadyToInteract()) LOG_DEBUG("Add the HandleInteractBouncer()!");
else SetupInteractBouncer();
break; break;
case PetInteractType::treasure: case PetInteractType::treasure:
StartInteractDig(); if (GetIsReadyToInteract()) HandleInteractTreasureDig();
else SetupInteractTreasureDig();
break; break;
default: default:
@ -983,11 +980,8 @@ void PetComponent::OnInteract() {
} }
} }
void PetComponent::OnTether() { void PetComponent::StartInteract(const NiPoint3 position, const PetInteractType interactType, const LWOOBJID interactID) {
m_Timer += 0.5f; SetInteraction(interactID); // TODO: Check if this should be serialized for goToObj
}
void PetComponent::StartInteract(NiPoint3 position, PetInteractType interactType) {
SetInteractType(interactType); SetInteractType(interactType);
SetAbility(PetAbilityType::GoToObject); SetAbility(PetAbilityType::GoToObject);
SetPetAiState(PetAiState::goToObj); SetPetAiState(PetAiState::goToObj);
@ -999,30 +993,68 @@ void PetComponent::StartInteract(NiPoint3 position, PetInteractType interactType
} }
void PetComponent::StopInteract() { void PetComponent::StopInteract() {
SetInteraction(LWOOBJID_EMPTY);
SetInteractType(PetInteractType::none); SetInteractType(PetInteractType::none);
SetAbility(PetAbilityType::Invalid); SetAbility(PetAbilityType::Invalid);
SetPetAiState(PetAiState::follow); SetPetAiState(PetAiState::follow);
SetStatus(PetStatus::NONE);
SetIsReadyToInteract(false);
m_MovementAI->SetMaxSpeed(m_SprintSpeed); m_MovementAI->SetMaxSpeed(m_SprintSpeed);
m_MovementAI->SetHaltDistance(m_FollowRadius); m_MovementAI->SetHaltDistance(m_FollowRadius);
LOG_DEBUG("Stopping interaction!"); LOG_DEBUG("Stopping interaction!");
Game::entityManager->SerializeEntity(m_Parent); Game::entityManager->SerializeEntity(m_Parent);
} }
void PetComponent::StartInteractBouncer() { void PetComponent::SetupInteractBouncer() {
SetAbility(PetAbilityType::JumpOnObject); // THIS IS ALL BAD, BAD, BAD! FIX IT, ME! >:(
/*SetAbility(PetAbilityType::JumpOnObject);
NiPoint3 destination = m_MovementAI->GetDestination(); NiPoint3 destination = m_MovementAI->GetDestination();
SwitchComponent* closestSwitch = SwitchComponent::GetClosestSwitch(destination); SwitchComponent* closestSwitch = SwitchComponent::GetClosestSwitch(destination);
m_Interaction = closestSwitch->GetParentEntity()->GetObjectID(); m_Interaction = closestSwitch->GetParentEntity()->GetObjectID();
Game::entityManager->SerializeEntity(m_Parent); Game::entityManager->SerializeEntity(m_Parent);
closestSwitch->EntityEnter(m_Parent); closestSwitch->EntityEnter(m_Parent);*/
} }
void PetComponent::StartInteractDig() { void PetComponent::SetupInteractTreasureDig() {
//m_InInteract = true; LOG_DEBUG("Setting up dig interaction!");
//m_TresureTime = 2.0f; //TODO: Remove magic number Entity* closestTreasure = Game::entityManager->GetEntity(GetInteraction());
m_Interaction == LWOOBJID_EMPTY; //TODO: Make this not empty if (!closestTreasure) return;
Command(NiPoint3::ZERO, LWOOBJID_EMPTY, 1, PetEmote::Bounce, true);\
m_Timer = 2.0f; 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::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) { 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(); auto* owner = GetOwner();
if (owner == nullptr) return; if (owner == nullptr) return;
SetStatus(1); SetStatus(PetStatus::PLAY_SPAWN_ANIM);
auto databaseData = inventoryComponent->GetDatabasePet(m_DatabaseId); 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(); activePets[m_Owner] = m_Parent->GetObjectID();
m_Timer = 3; //m_Timer = 3;
Game::entityManager->SerializeEntity(m_Parent); Game::entityManager->SerializeEntity(m_Parent);

View File

@ -32,6 +32,7 @@ enum class PetInteractType : uint8_t {
*/ */
enum PetStatus : uint32_t { enum PetStatus : uint32_t {
NONE, NONE,
//READY_TO_DIG,
BEING_TAMED = 0x10, BEING_TAMED = 0x10,
IS_NOT_WAITING = 0x20, IS_NOT_WAITING = 0x20,
PLAY_SPAWN_ANIM = 0x80, PLAY_SPAWN_ANIM = 0x80,
@ -148,15 +149,10 @@ public:
*/ */
void OnInteract(); 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 * 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 * Stop a pet interaction with an object
@ -268,42 +264,35 @@ public:
void SetPreconditions(std::string& conditions); void SetPreconditions(std::string& conditions);
/** /**
* Sets if the pet is ready to dig * Sets if the pet is ready to interact with an object
* @param isReady whether the pet is ready to dig (true) or not (false) * @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(); void SetupInteractBouncer();
/** /**
* Start the treasure dig interaction * Set up the treasure dig interaction
*/ */
void StartInteractDig(); void SetupInteractTreasureDig();
/** /**
* Handles the pet dig interaction * Starts the pet treasure dig interaction
* @param deltaTime time elapsed */
*/ void StartInteractTreasureDig();
void InteractDig(float deltaTime);
/** /**
* End the dig interaction * Handles the pet treasure dig interaction
*/ */
void EndInteractDig(); void HandleInteractTreasureDig();
/**
* 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 * Returns the entity that this component belongs to
@ -479,15 +468,14 @@ private:
float m_TimerAway; float m_TimerAway;
/** /**
* Timer that tracks how long a pet has been digging up some treasure, required to spawn the treasure contents * A timer that tracks how long until a tamed pet will bounce again when standing over a treasure dig site
* on time */
*/ float m_TimerBounce;
float m_TresureTime;
/** /**
* 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 * The position that this pet was spawned at
@ -497,7 +485,7 @@ private:
/** /**
* The halting radius of the pet while following a player * 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 * The movement AI component that is related to this pet, required to move it around

View File

@ -108,14 +108,14 @@ void PetDigServer::OnUse(Entity* self, Entity* user) {
auto* petComponent = PetComponent::GetActivePet(user->GetObjectID()); auto* petComponent = PetComponent::GetActivePet(user->GetObjectID());
if (!petComponent) return; 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<DestroyableComponent>(); auto* destroyableComponent = user->GetComponent<DestroyableComponent>();
if (!destroyableComponent) return; if (!destroyableComponent) return;
auto imagination = destroyableComponent->GetImagination(); 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 (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 imagination -= 1; // TODO: Get rid of this magic number
destroyableComponent->SetImagination(imagination); destroyableComponent->SetImagination(imagination);