pet bouncer minimum functionality

This commit is contained in:
jadebenn 2023-12-27 19:39:07 -06:00
parent d7d5202c17
commit 6edd42d37c
3 changed files with 159 additions and 117 deletions

View File

@ -76,7 +76,7 @@ std::map<LOT, int32_t> PetComponent::petFlags = {
{ 13067, 838 }, // Skeleton dragon { 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_ComponentId = componentId;
m_Interaction = LWOOBJID_EMPTY; m_Interaction = LWOOBJID_EMPTY;
@ -109,25 +109,13 @@ PetComponent::PetComponent(Entity* parent, uint32_t componentId): Component(pare
m_FollowRadius = Game::zoneManager->GetPetFollowRadius(); m_FollowRadius = Game::zoneManager->GetPetFollowRadius();
} }
bool PetComponent::LoadPetInfo(uint32_t petId, CDPetComponent& result) {
CDPetComponentTable* petTable;
petTable = CDClientManager::Instance().GetTable<CDPetComponentTable>();
const auto pet = petTable->GetByID(petId);
if (!pet) return false;
result = pet[0];
return true;
}
void PetComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) { void PetComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) {
const bool tamed = m_Owner != LWOOBJID_EMPTY; const bool tamed = m_Owner != LWOOBJID_EMPTY;
outBitStream->Write1(); // Always serialize as dirty for now outBitStream->Write1(); // Always serialize as dirty for now
outBitStream->Write<uint32_t>(static_cast<unsigned int>(m_Flags)); outBitStream->Write<uint32_t>(static_cast<unsigned int>(m_Flags));
outBitStream->Write<uint32_t>(static_cast<uint32_t>(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; const bool interacting = m_Interaction != LWOOBJID_EMPTY;
@ -177,7 +165,7 @@ void PetComponent::OnUse(Entity* originator) {
break; break;
case ePetAbilityType::JumpOnObject: // Bouncer case ePetAbilityType::JumpOnObject: // Bouncer
//StartInteractBouncer(); StartInteractBouncer();
break; break;
default: default:
@ -350,12 +338,6 @@ void PetComponent::OnUse(Entity* originator) {
} }
void PetComponent::Update(float deltaTime) { 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 // Update timers
m_TimerBounce -= deltaTime; m_TimerBounce -= deltaTime;
@ -398,8 +380,7 @@ void PetComponent::Update(float deltaTime) {
LOG_DEBUG("Reached object!"); LOG_DEBUG("Reached object!");
m_MovementAI->Stop(); m_MovementAI->Stop();
SetPetAiState(PetAiState::interact); SetPetAiState(PetAiState::interact);
} } else {
else {
m_Timer += 0.5f; m_Timer += 0.5f;
} }
break; break;
@ -412,24 +393,6 @@ void PetComponent::Update(float deltaTime) {
LOG_DEBUG("Unknown state: %d!", m_Flags); LOG_DEBUG("Unknown state: %d!", m_Flags);
break; 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 void PetComponent::UpdateUnowned(float deltaTime) { //TODO: CURRENTLY UNUSED
@ -443,8 +406,7 @@ void PetComponent::UpdateUnowned(float deltaTime) { //TODO: CURRENTLY UNUSED
ClientFailTamingMinigame(); ClientFailTamingMinigame();
} }
} }
} } else {
else {
if (m_Timer > 0) { if (m_Timer > 0) {
m_Timer -= deltaTime; m_Timer -= deltaTime;
@ -798,17 +760,17 @@ void PetComponent::Wander() {
return; return;
} }
m_MovementAI->SetMaxSpeed(m_PetInfo.sprintSpeed); //info.wanderSpeed); m_MovementAI->SetMaxSpeed(m_PetInfo->sprintSpeed); //info.wanderSpeed);
m_MovementAI->SetDestination(destination); 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() { void PetComponent::OnSpawn() {
if (!LoadPetInfo(m_ComponentId, m_PetInfo)) { m_PetInfo = CDClientManager::Instance().GetTable<CDPetComponentTable>()->GetByID(m_ComponentId);
LOG("Failed to load PetComponent (id: %d) information from CDClient!", m_ComponentId); if (!m_PetInfo) LOG("Failed to load PetComponent (id: %d) information from CDClient!", m_ComponentId);
}
m_MovementAI = m_Parent->GetComponent<MovementAIComponent>(); m_MovementAI = m_Parent->GetComponent<MovementAIComponent>();
if (m_StartPosition == NiPoint3::ZERO) { if (m_StartPosition == NiPoint3::ZERO) {
@ -817,12 +779,11 @@ void PetComponent::OnSpawn() {
if (m_Owner != LWOOBJID_EMPTY) { if (m_Owner != LWOOBJID_EMPTY) {
m_Parent->SetOwnerOverride(m_Owner); m_Parent->SetOwnerOverride(m_Owner);
m_MovementAI->SetMaxSpeed(m_PetInfo.sprintSpeed); m_MovementAI->SetMaxSpeed(m_PetInfo->sprintSpeed);
m_MovementAI->SetHaltDistance(m_FollowRadius); m_MovementAI->SetHaltDistance(m_FollowRadius);
//SetOnlyFlag(IDLE); //SetStatus(PetFlag::NONE); //SetOnlyFlag(IDLE); //SetStatus(PetFlag::NONE);
SetPetAiState(PetAiState::follow); SetPetAiState(PetAiState::follow);
} } else {
else {
SetFlag(TAMEABLE); SetFlag(TAMEABLE);
SetPetAiState(PetAiState::idle); SetPetAiState(PetAiState::idle);
} }
@ -873,8 +834,7 @@ void PetComponent::OnFollow(const float& deltaTime) {
// If the player's position is within range, stop moving // If the player's position is within range, stop moving
if (distanceToOwner <= m_FollowRadius * m_FollowRadius) { if (distanceToOwner <= m_FollowRadius * m_FollowRadius) {
m_MovementAI->Stop(); m_MovementAI->Stop();
} } else { // Chase the player's new position
else { // Chase the player's new position
m_MovementAI->SetDestination(ownerPos); m_MovementAI->SetDestination(ownerPos);
} }
@ -886,8 +846,7 @@ void PetComponent::OnFollow(const float& deltaTime) {
m_TimerAway = 0; m_TimerAway = 0;
return; 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; m_TimerAway += deltaTime;
} }
@ -910,7 +869,7 @@ void PetComponent::OnInteract() {
switch (GetInteractType()) { switch (GetInteractType()) {
case PetInteractType::bouncer: case PetInteractType::bouncer:
if (IsReadyToInteract()) LOG_DEBUG("Add the HandleInteractBouncer()!"); if (IsReadyToInteract()) HandleInteractBouncer();
else SetupInteractBouncer(); else SetupInteractBouncer();
break; break;
@ -932,7 +891,7 @@ void PetComponent::StartInteract(const NiPoint3 position, const PetInteractType
SetInteractType(interactType); SetInteractType(interactType);
SetAbility(ePetAbilityType::GoToObject); SetAbility(ePetAbilityType::GoToObject);
SetPetAiState(PetAiState::goToObj); SetPetAiState(PetAiState::goToObj);
m_MovementAI->SetMaxSpeed(m_PetInfo.runSpeed); m_MovementAI->SetMaxSpeed(m_PetInfo->runSpeed);
m_MovementAI->SetHaltDistance(0.0f); m_MovementAI->SetHaltDistance(0.0f);
m_MovementAI->SetDestination(position); m_MovementAI->SetDestination(position);
LOG_DEBUG("Starting interaction!"); LOG_DEBUG("Starting interaction!");
@ -952,7 +911,7 @@ void PetComponent::StopInteract(bool bDontSerialize) {
SetOnlyFlag(IDLE); //SetStatus(PetFlag::NONE); SetOnlyFlag(IDLE); //SetStatus(PetFlag::NONE);
SetIsReadyToInteract(false); SetIsReadyToInteract(false);
SetIsHandlingInteraction(false); // Needed? SetIsHandlingInteraction(false); // Needed?
m_MovementAI->SetMaxSpeed(m_PetInfo.sprintSpeed); m_MovementAI->SetMaxSpeed(m_PetInfo->sprintSpeed);
m_MovementAI->SetHaltDistance(m_FollowRadius); m_MovementAI->SetHaltDistance(m_FollowRadius);
LOG_DEBUG("Stopping interaction!"); LOG_DEBUG("Stopping interaction!");
@ -964,32 +923,107 @@ void PetComponent::StopInteract(bool bDontSerialize) {
} }
void PetComponent::SetupInteractBouncer() { 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<DestroyableComponent>();
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! >:( // THIS IS ALL BAD, BAD, BAD! FIX IT, ME! >:(
/*SetAbility(ePetAbilityType::JumpOnObject); SetIsHandlingInteraction(true);
NiPoint3 destination = m_MovementAI->GetDestination(); SwitchComponent* closestSwitch = SwitchComponent::GetClosestSwitch(m_MovementAI->GetDestination()); // TODO: Find a better way to do this
SwitchComponent* closestSwitch = SwitchComponent::GetClosestSwitch(destination); closestSwitch->EntityEnter(m_Parent);
m_Interaction = closestSwitch->GetParentEntity()->GetObjectID();
Game::entityManager->SerializeEntity(m_Parent); //m_Timer += 0.5;
closestSwitch->EntityEnter(m_Parent);*/ }
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() { void PetComponent::SetupInteractTreasureDig() {
auto* owner = GetOwner(); const auto* owner = GetOwner();
if (!owner) return; if (!owner) return;
LOG_DEBUG("Setting up dig interaction!"); LOG_DEBUG("Setting up dig interaction!");
SetIsReadyToInteract(true); SetIsReadyToInteract(true);
auto petAbility = ePetAbilityType::DigAtPosition; const auto petAbility = ePetAbilityType::DigAtPosition;
SetAbility(petAbility); SetAbility(petAbility);
UnsetFlag(IDLE); 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); LOG_DEBUG("m_Flags = %d", m_Flags);
Game::entityManager->SerializeEntity(m_Parent); // TODO: Double-check pet packet captures Game::entityManager->SerializeEntity(m_Parent); // TODO: Double-check pet packet captures
const auto sysAddr = owner->GetSystemAddress(); const auto sysAddr = owner->GetSystemAddress();
GameMessages::SendHelp(m_Owner, eHelpType::PR_DIG_TUTORIAL_01, sysAddr); GameMessages::SendHelp(m_Owner, eHelpType::PR_DIG_TUTORIAL_01, sysAddr);
GameMessages::SendShowPetActionButton(m_Owner, petAbility, true, sysAddr); GameMessages::SendShowPetActionButton(m_Owner, petAbility, true, sysAddr);
m_Timer += 0.5f; m_Timer += 0.5f;
} }
@ -1001,7 +1035,7 @@ void PetComponent::StartInteractTreasureDig() {
if (!destroyableComponent) return; if (!destroyableComponent) return;
auto imagination = destroyableComponent->GetImagination(); 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) { if (imagination < imaginationCost) {
//GameMessages::SendHelp(user->GetObjectID(), eHelpType::PR_NEED_IMAGINATION, user->GetSystemAddress()); // Check if right message! //GameMessages::SendHelp(user->GetObjectID(), eHelpType::PR_NEED_IMAGINATION, user->GetSystemAddress()); // Check if right message!
return; return;
@ -1014,7 +1048,7 @@ void PetComponent::StartInteractTreasureDig() {
Game::entityManager->SerializeEntity(user); Game::entityManager->SerializeEntity(user);
SetIsHandlingInteraction(true); 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); SetFlag(IDLE);
LOG_DEBUG("StartInteractTreasureDig() m_Flags = %d", m_Flags); LOG_DEBUG("StartInteractTreasureDig() m_Flags = %d", m_Flags);
Game::entityManager->SerializeEntity(m_Parent); Game::entityManager->SerializeEntity(m_Parent);
@ -1025,10 +1059,10 @@ void PetComponent::StartInteractTreasureDig() {
void PetComponent::HandleInteractTreasureDig() { void PetComponent::HandleInteractTreasureDig() {
if (IsHandlingInteraction()) { if (IsHandlingInteraction()) {
auto* owner = GetOwner(); auto* const owner = GetOwner();
if (!owner) return; 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()); treasure->Smash(m_Parent->GetObjectID());
GameMessages::SendHelp(m_Owner, eHelpType::PR_DIG_TUTORIAL_03, owner->GetSystemAddress()); GameMessages::SendHelp(m_Owner, eHelpType::PR_DIG_TUTORIAL_03, owner->GetSystemAddress());
@ -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 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<CDPetComponentTable>()->GetByID(m_ComponentId);
if (!m_PetInfo) LOG("Failed to load PetComponent (id: %d) information from CDClient!", m_ComponentId);
AddDrainImaginationTimer(item, fromTaming); AddDrainImaginationTimer(item, fromTaming);
m_ItemId = item->GetId(); m_ItemId = item->GetId();
@ -1129,7 +1166,7 @@ void PetComponent::AddDrainImaginationTimer(Item* item, bool fromTaming) {
if (!fromTaming) playerDestroyableComponent->Imagine(-1); 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. // 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) { if (!playerDestroyableComponent) {
LOG("No petComponent and/or no playerDestroyableComponent"); LOG("No petComponent and/or no playerDestroyableComponent");
return; return;
@ -1303,7 +1340,7 @@ void PetComponent::SetPetNameForModeration(const std::string& petName) {
} }
//Save to db: //Save to db:
Database::Get()->SetPetNameModerationStatus(m_DatabaseId, IPetNames::Info{petName, approved}); Database::Get()->SetPetNameModerationStatus(m_DatabaseId, IPetNames::Info{ petName, approved });
} }
void PetComponent::LoadPetNameFromModeration() { void PetComponent::LoadPetNameFromModeration() {

View File

@ -39,9 +39,9 @@ enum PetFlag : uint32_t {
UNKNOWN4 = 1 << 2, //0x04 - FOLLOWING(?) UNKNOWN4 = 1 << 2, //0x04 - FOLLOWING(?)
BEING_TAMED = 1 << 4, //0x10, BEING_TAMED = 1 << 4, //0x10,
NOT_WAITING = 1 << 5, //0x20, 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 SPAWNING = 1 << 7, //0x80
UNKNOWN256 = 1 << 8, //0x100 ON_SWITCH = 1 << 8, //0x100
UNKNOWN1024 = 1 << 10, //0x400 UNKNOWN1024 = 1 << 10, //0x400
TAMEABLE = 1 << 26 //0x4000000 TAMEABLE = 1 << 26 //0x4000000
}; };
@ -70,11 +70,6 @@ public:
PetComponent(Entity* parentEntity, uint32_t componentId); PetComponent(Entity* parentEntity, uint32_t componentId);
~PetComponent() override; ~PetComponent() override;
/**
* Loads pet info from CDClient
*/
bool LoadPetInfo(uint32_t petId, CDPetComponent& result);
/** /**
* Serializes the pet * Serializes the pet
* @param outBitStream The output bitstream * @param outBitStream The output bitstream
@ -346,6 +341,16 @@ public:
*/ */
void SetupInteractBouncer(); void SetupInteractBouncer();
/**
* Starts the pet bouncer interaction
*/
void StartInteractBouncer();
/**
* Handles the pet bouncer interaction
*/
void HandleInteractBouncer();
/** /**
* Set up the treasure dig interaction * Set up the treasure dig interaction
*/ */
@ -437,7 +442,7 @@ private:
/** /**
* Pet information loaded from the CDClientDatabase * Pet information loaded from the CDClientDatabase
*/ */
CDPetComponent m_PetInfo; CDPetComponent* m_PetInfo;
/** /**
* Cache of all the pets that are currently spawned, indexed by tamer * Cache of all the pets that are currently spawned, indexed by tamer

View File

@ -60,8 +60,8 @@ void SwitchComponent::EntityEnter(Entity* entity) {
if (m_PetBouncer != nullptr) { if (m_PetBouncer != nullptr) {
GameMessages::SendPlayFXEffect(m_Parent->GetObjectID(), 2602, u"pettriggeractive", "BounceEffect", LWOOBJID_EMPTY, 1, 1, true); GameMessages::SendPlayFXEffect(m_Parent->GetObjectID(), 2602, u"pettriggeractive", "BounceEffect", LWOOBJID_EMPTY, 1, 1, true);
RenderComponent::PlayAnimation(m_Parent, u"engaged"); //RenderComponent::PlayAnimation(m_Parent, u"engaged");
m_PetBouncer->SetPetBouncerEnabled(true); //m_PetBouncer->SetPetBouncerEnabled(true);
} else { } else {
Game::entityManager->SerializeEntity(m_Parent); Game::entityManager->SerializeEntity(m_Parent);
} }