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

View File

@ -39,9 +39,9 @@ enum PetFlag : uint32_t {
UNKNOWN4 = 1 << 2, //0x04 - FOLLOWING(?)
BEING_TAMED = 1 << 4, //0x10,
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
UNKNOWN256 = 1 << 8, //0x100
ON_SWITCH = 1 << 8, //0x100
UNKNOWN1024 = 1 << 10, //0x400
TAMEABLE = 1 << 26 //0x4000000
};
@ -70,11 +70,6 @@ public:
PetComponent(Entity* parentEntity, uint32_t componentId);
~PetComponent() override;
/**
* Loads pet info from CDClient
*/
bool LoadPetInfo(uint32_t petId, CDPetComponent& result);
/**
* Serializes the pet
* @param outBitStream The output bitstream
@ -346,6 +341,16 @@ public:
*/
void SetupInteractBouncer();
/**
* Starts the pet bouncer interaction
*/
void StartInteractBouncer();
/**
* Handles the pet bouncer interaction
*/
void HandleInteractBouncer();
/**
* Set up the treasure dig interaction
*/
@ -437,7 +442,7 @@ private:
/**
* Pet information loaded from the CDClientDatabase
*/
CDPetComponent m_PetInfo;
CDPetComponent* m_PetInfo;
/**
* 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) {
GameMessages::SendPlayFXEffect(m_Parent->GetObjectID(), 2602, u"pettriggeractive", "BounceEffect", LWOOBJID_EMPTY, 1, 1, true);
RenderComponent::PlayAnimation(m_Parent, u"engaged");
m_PetBouncer->SetPetBouncerEnabled(true);
//RenderComponent::PlayAnimation(m_Parent, u"engaged");
//m_PetBouncer->SetPetBouncerEnabled(true);
} else {
Game::entityManager->SerializeEntity(m_Parent);
}