added treasure dig menu prompts and help messages

This commit is contained in:
jadebenn 2023-12-14 01:33:49 -06:00
parent 2a4cfe3447
commit 1b8f10fcfb
10 changed files with 145 additions and 82 deletions

View File

@ -1,6 +1,4 @@
#ifndef __EHELPTYPE__H__
#define __EHELPTYPE__H__
#pragma once
#include <cstdint>
@ -37,5 +35,3 @@ enum class eHelpType : int32_t {
PET_DESPAWN_TAMING_NEW_PET = 70,
UI_INVENTORY_FULL_CANNOT_PICKUP_ITEM = 86
};
#endif //!__EHELPTYPE__H__

View File

@ -0,0 +1,10 @@
#pragma once
#include <cstdint>
enum class ePetAbilityType : int32_t {
Invalid,
GoToObject,
JumpOnObject,
DigAtPosition
};

View File

@ -1,5 +1,4 @@
#ifndef BASECOMBATAICOMPONENT_H
#define BASECOMBATAICOMPONENT_H
#pragma once
#include "RakNetTypes.h"
#include "dCommonVars.h"
@ -388,5 +387,3 @@ private:
*/
bool IsMech();
};
#endif // BASECOMBATAICOMPONENT_H

View File

@ -17,12 +17,15 @@
#include "eUnequippableActiveType.h"
#include "eTerminateType.h"
#include "ePetTamingNotifyType.h"
#include "ePetAbilityType.h"
#include "eUseItemResponse.h"
#include "ePlayerFlag.h"
#include "eHelpType.h"
#include "Game.h"
#include "dConfig.h"
#include "dChatFilter.h"
#include "dZoneManager.h"
#include "Database.h"
#include "EntityInfo.h"
#include "eMissionTaskType.h"
@ -34,6 +37,7 @@
std::unordered_map<LOT, PetComponent::PetPuzzleData> PetComponent::buildCache{};
std::unordered_map<LWOOBJID, LWOOBJID> PetComponent::currentActivities{};
std::unordered_map<LWOOBJID, LWOOBJID> PetComponent::activePets{};
float PetComponent::m_FollowRadius{};
/**
* Maps all the pet lots to a flag indicating that the player has caught it. All basic pets have been guessed by ObjID
@ -83,14 +87,15 @@ PetComponent::PetComponent(Entity* parent, uint32_t componentId): Component(pare
m_TimerBounce = 0;
m_DatabaseId = LWOOBJID_EMPTY;
m_Status = PetStatus::TAMEABLE; // Tameable
m_Ability = PetAbilityType::Invalid;
m_Ability = ePetAbilityType::Invalid;
m_StartPosition = m_Parent->GetPosition(); //NiPoint3::ZERO;
m_MovementAI = nullptr;
m_Preconditions = nullptr;
m_ReadyToInteract = false;
SetPetAiState(PetAiState::spawn);
//m_FollowRadius = 8.0f;
m_FollowRadius = Game::zoneManager->GetPetFollowRadius();
SetIsHandlingInteraction(false);
std::string checkPreconditions = GeneralUtils::UTF16ToWTF8(parent->GetVar<std::u16string>(u"CheckPrecondition"));
@ -121,7 +126,7 @@ void PetComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpd
outBitStream->Write1(); // Always serialize as dirty for now
outBitStream->Write<uint32_t>(static_cast<unsigned int>(m_Status));
outBitStream->Write<uint32_t>(static_cast<uint32_t>(tamed ? m_Ability : PetAbilityType::Invalid)); // Something with the overhead icon?
outBitStream->Write<uint32_t>(static_cast<uint32_t>(tamed ? m_Ability : ePetAbilityType::Invalid)); // Something with the overhead icon?
const bool interacting = m_Interaction != LWOOBJID_EMPTY;
@ -164,6 +169,23 @@ void PetComponent::SetPetAiState(PetAiState newState) {
void PetComponent::OnUse(Entity* originator) {
LOG("PET USE!");
if (IsReadyToInteract()) {
switch (GetAbility()) {
case ePetAbilityType::DigAtPosition: // Treasure dig TODO: FIX ICON
StartInteractTreasureDig();
break;
case ePetAbilityType::JumpOnObject: // Bouncer
//StartInteractBouncer();
break;
default:
break;
}
}
// TODO: Rewrite everything below this comment
if (m_Owner != LWOOBJID_EMPTY) return;
if (m_Tamer != LWOOBJID_EMPTY) {
@ -526,10 +548,7 @@ void PetComponent::NotifyTamingBuildSuccess(NiPoint3 position) {
GameMessages::SendPetResponse(m_Tamer, m_Parent->GetObjectID(), 0, 10, 0, tamer->GetSystemAddress());
auto* inventoryComponent = tamer->GetComponent<InventoryComponent>();
if (inventoryComponent == nullptr) {
return;
}
if (!inventoryComponent) return;
LWOOBJID petSubKey = ObjectIDManager::Instance()->GenerateRandomObjectID();
@ -548,11 +567,9 @@ void PetComponent::NotifyTamingBuildSuccess(NiPoint3 position) {
GameMessages::SendRegisterPetDBID(m_Tamer, petSubKey, tamer->GetSystemAddress());
inventoryComponent->AddItem(m_Parent->GetLOT(), 1, eLootSourceType::ACTIVITY, eInventoryType::MODELS, {}, LWOOBJID_EMPTY, true, false, petSubKey);
auto* item = inventoryComponent->FindItemBySubKey(petSubKey, MODELS);
if (item == nullptr) {
return;
}
if (!item) return;
DatabasePet databasePet{};
@ -626,10 +643,7 @@ void PetComponent::RequestSetPetName(std::u16string name) {
LOG("Got set pet name (%s)", GeneralUtils::UTF16ToWTF8(name).c_str());
auto* inventoryComponent = tamer->GetComponent<InventoryComponent>();
if (inventoryComponent == nullptr) {
return;
}
if (!inventoryComponent) return;
m_ModerationStatus = 1; // Pending
m_Name = "";
@ -769,9 +783,7 @@ void PetComponent::ClientFailTamingMinigame() {
}
void PetComponent::Wander() {
//m_MovementAI = m_Parent->GetComponent<MovementAIComponent>();
if (/*m_MovementAI == nullptr ||*/ !m_MovementAI->AtFinalWaypoint()) return;
if (!m_MovementAI->AtFinalWaypoint()) return;
m_MovementAI->SetHaltDistance(0);
@ -916,7 +928,7 @@ void PetComponent::OnInteract() {
void PetComponent::StartInteract(const NiPoint3 position, const PetInteractType interactType, const LWOOBJID interactID) {
SetInteraction(interactID); // TODO: Check if this should be serialized for goToObj
SetInteractType(interactType);
SetAbility(PetAbilityType::GoToObject);
SetAbility(ePetAbilityType::GoToObject);
SetPetAiState(PetAiState::goToObj);
m_MovementAI->SetMaxSpeed(m_RunSpeed);
m_MovementAI->SetHaltDistance(0.0f);
@ -926,21 +938,28 @@ void PetComponent::StartInteract(const NiPoint3 position, const PetInteractType
}
void PetComponent::StopInteract() {
Entity* owner = GetOwner();
if (!owner) return;
const auto petAbility = ePetAbilityType::Invalid;
SetInteraction(LWOOBJID_EMPTY);
SetInteractType(PetInteractType::none);
SetAbility(PetAbilityType::Invalid);
SetAbility(petAbility);
SetPetAiState(PetAiState::follow);
SetStatus(PetStatus::NONE);
SetIsReadyToInteract(false);
SetIsHandlingInteraction(false); // Needed?
m_MovementAI->SetMaxSpeed(m_SprintSpeed);
m_MovementAI->SetHaltDistance(m_FollowRadius);
LOG_DEBUG("Stopping interaction!");
Game::entityManager->SerializeEntity(m_Parent);
GameMessages::SendShowPetActionButton(m_Owner, petAbility, false, owner->GetSystemAddress()); // Needed?
}
void PetComponent::SetupInteractBouncer() {
// THIS IS ALL BAD, BAD, BAD! FIX IT, ME! >:(
/*SetAbility(PetAbilityType::JumpOnObject);
/*SetAbility(ePetAbilityType::JumpOnObject);
NiPoint3 destination = m_MovementAI->GetDestination();
SwitchComponent* closestSwitch = SwitchComponent::GetClosestSwitch(destination);
m_Interaction = closestSwitch->GetParentEntity()->GetObjectID();
@ -949,37 +968,63 @@ void PetComponent::SetupInteractBouncer() {
}
void PetComponent::SetupInteractTreasureDig() {
auto* owner = GetOwner();
if (!owner) return;
LOG_DEBUG("Setting up dig interaction!");
Entity* closestTreasure = Game::entityManager->GetEntity(GetInteraction());
if (!closestTreasure) return;
SetIsReadyToInteract(true);
auto petAbility = ePetAbilityType::DigAtPosition;
SetAbility(PetAbilityType::JumpOnObject);
SetAbility(petAbility);
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
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;
}
void PetComponent::StartInteractTreasureDig() {
SetAbility(PetAbilityType::DigAtPosition);
Entity* user = GetOwner();
if (IsHandlingInteraction() || !user) return;
auto* destroyableComponent = user->GetComponent<DestroyableComponent>();
if (!destroyableComponent) return;
auto imagination = destroyableComponent->GetImagination();
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;
}
GameMessages::SendShowPetActionButton(m_Owner, ePetAbilityType::Invalid, false, user->GetSystemAddress());
imagination -= imaginationCost;
destroyableComponent->SetImagination(imagination);
Game::entityManager->SerializeEntity(user);
SetIsHandlingInteraction(true);
auto newStatus = GeneralUtils::ClearBit(GetStatus(), 6);
SetStatus(newStatus); // TODO: FIND THE CORRECT STATUS TO USE HERE
Game::entityManager->SerializeEntity(m_Parent);
Command(NiPoint3::ZERO, LWOOBJID_EMPTY, 1, PetEmote::DigTreasure, true); // Plays 'dig' animation
m_Timer = 1.5f;
m_Timer = 2.0f;
}
void PetComponent::HandleInteractTreasureDig() {
if (GetAbility() == PetAbilityType::DigAtPosition) {
if (IsHandlingInteraction()) {
auto* owner = GetOwner();
auto* treasure = Game::entityManager->GetEntity(GetInteraction());
if (!treasure) return;
if (!treasure || !owner) return;
treasure->Smash(m_Parent->GetObjectID());
LOG_DEBUG("Pet dig completed!");
GameMessages::SendHelp(m_Owner, eHelpType::PR_DIG_TUTORIAL_03, owner->GetSystemAddress());
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;
//m_Timer = 1.5f;
return;
}
@ -991,7 +1036,7 @@ void PetComponent::HandleInteractTreasureDig() {
m_Timer += 0.5f;
}
void PetComponent::Activate(Item* item, bool registerPet, bool fromTaming) {
void PetComponent::Activate(Item* item, bool registerPet, bool fromTaming) { // TODO: Offset spawn position so it's not on top of player char
AddDrainImaginationTimer(item, fromTaming);
m_ItemId = item->GetId();
@ -1041,8 +1086,6 @@ void PetComponent::Activate(Item* item, bool registerPet, bool fromTaming) {
activePets[m_Owner] = m_Parent->GetObjectID();
//m_Timer = 3;
Game::entityManager->SerializeEntity(m_Parent);
owner->GetCharacter()->SetPlayerFlag(ePlayerFlag::FIRST_MANUAL_PET_HIBERNATE, true);
@ -1054,8 +1097,6 @@ void PetComponent::Activate(Item* item, bool registerPet, bool fromTaming) {
GameMessages::SendRegisterPetDBID(m_Owner, m_DatabaseId, owner->GetSystemAddress());
}
GameMessages::SendShowPetActionButton(m_Owner, 3, true, owner->GetSystemAddress());
}
void PetComponent::AddDrainImaginationTimer(Item* item, bool fromTaming) {
@ -1115,7 +1156,7 @@ void PetComponent::Deactivate() {
GameMessages::SendRegisterPetDBID(m_Owner, LWOOBJID_EMPTY, owner->GetSystemAddress());
GameMessages::SendShowPetActionButton(m_Owner, 0, false, owner->GetSystemAddress());
GameMessages::SendShowPetActionButton(m_Owner, ePetAbilityType::Invalid, false, owner->GetSystemAddress());
}
void PetComponent::Release() {
@ -1182,7 +1223,7 @@ uint32_t PetComponent::GetStatus() const {
return m_Status;
}
PetAbilityType PetComponent::GetAbility() const {
ePetAbilityType PetComponent::GetAbility() const {
return m_Ability;
}
@ -1192,9 +1233,10 @@ void PetComponent::SetInteraction(LWOOBJID value) {
void PetComponent::SetStatus(uint32_t value) {
m_Status = value;
LOG_DEBUG("Pet status set to: %x", m_Status);
}
void PetComponent::SetAbility(PetAbilityType value) {
void PetComponent::SetAbility(ePetAbilityType value) {
m_Ability = value;
}

View File

@ -5,6 +5,7 @@
#include "Component.h"
#include "Preconditions.h"
#include "eReplicaComponentType.h"
#include "ePetAbilityType.h"
/*
* The current state of the pet AI
@ -45,13 +46,6 @@ enum PetEmote : int32_t {
Bounce
};
enum class PetAbilityType {
Invalid,
GoToObject,
JumpOnObject,
DigAtPosition
};
/**
* Represents an entity that is a pet. This pet can be tamed and consequently follows the tamer around, allowing it
* to dig for treasure and activate pet bouncers.
@ -249,13 +243,13 @@ public:
* Returns an ability the pet may perform, currently unused
* @return an ability the pet may perform
*/
PetAbilityType GetAbility() const;
ePetAbilityType GetAbility() const;
/**
* Sets the ability of the pet, currently unused
* @param value the ability to set
*/
void SetAbility(PetAbilityType value);
void SetAbility(ePetAbilityType value);
/**
* Sets preconditions for the pet that need to be met before it can be tamed
@ -274,6 +268,17 @@ public:
*/
bool IsReadyToInteract() { return m_ReadyToInteract; };
/**
* Sets if the pet is currently handling an interaction with an object
* @param isHandlingInteraction whether the pet is currently handling an interaction with an object
*/
void SetIsHandlingInteraction(bool isHandlingInteraction) { m_IsHandlingInteraction = isHandlingInteraction; };
/**
* @return is pet currently handling an interaction with an object
*/
bool IsHandlingInteraction() { return m_IsHandlingInteraction; };
/**
* Set up the pet bouncer interaction
*/
@ -387,6 +392,11 @@ private:
*/
static std::map<LOT, int32_t> petFlags;
/**
* The halting radius of the pet while following a player
*/
static float m_FollowRadius;
/**
* The ID of the component in the pet component table
*/
@ -455,7 +465,7 @@ private:
/**
* A currently active ability, mostly unused
*/
PetAbilityType m_Ability;
ePetAbilityType m_Ability;
/**
* The time an entity has left to complete the minigame
@ -477,16 +487,16 @@ private:
*/
bool m_ReadyToInteract;
/**
* Boolean that sets if a pet is currently handling an interaction with an object
*/
bool m_IsHandlingInteraction;
/**
* The position that this pet was spawned at
*/
NiPoint3 m_StartPosition;
/**
* The halting radius of the pet while following a player
*/
const float m_FollowRadius = 8.0f;
/**
* The movement AI component that is related to this pet, required to move it around
*/

View File

@ -44,6 +44,8 @@
#include "eStateChangeType.h"
#include "eConnectionType.h"
#include "ePlayerFlag.h"
#include "eHelpType.h"
#include "ePetAbilityType.h"
#include <sstream>
#include <future>
@ -505,6 +507,17 @@ void GameMessages::SendNotifyClientFlagChange(const LWOOBJID& objectID, uint32_t
SEND_PACKET;
}
void GameMessages::SendHelp(const LWOOBJID& objectID, const eHelpType help, const SystemAddress& sysAddr) {
CBITSTREAM;
CMSGHEADER;
bitStream.Write(objectID);
bitStream.Write(eGameMessageType::HELP);
bitStream.Write(help);
SEND_PACKET;
}
void GameMessages::SendChangeObjectWorldState(const LWOOBJID& objectID, eObjectWorldState state, const SystemAddress& sysAddr) {
CBITSTREAM;
CMSGHEADER;
@ -3508,14 +3521,14 @@ void GameMessages::SendClientExitTamingMinigame(LWOOBJID objectId, bool bVolunta
SEND_PACKET;
}
void GameMessages::SendShowPetActionButton(LWOOBJID objectId, int32_t buttonLabel, bool bShow, const SystemAddress& sysAddr) {
void GameMessages::SendShowPetActionButton(const LWOOBJID& objectId, const ePetAbilityType petAbility, bool bShow, const SystemAddress& sysAddr) {
CBITSTREAM;
CMSGHEADER;
bitStream.Write(objectId);
bitStream.Write(eGameMessageType::SHOW_PET_ACTION_BUTTON);
bitStream.Write(buttonLabel);
bitStream.Write(petAbility);
bitStream.Write(bShow);
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;

View File

@ -1,5 +1,4 @@
#ifndef GAMEMESSAGES_H
#define GAMEMESSAGES_H
#pragma once
#include "dCommonVars.h"
#include <map>
@ -10,6 +9,8 @@
#include "eEndBehavior.h"
#include "eCyclingMode.h"
#include "eLootSourceType.h"
#include "eHelpType.h"
#include "ePetAbilityType.h"
#include "Brick.h"
class AMFBaseValue;
@ -81,6 +82,7 @@ namespace GameMessages {
void SendAddItemToInventoryClientSync(Entity* entity, const SystemAddress& sysAddr, Item* item, const LWOOBJID& objectID, bool showFlyingLoot, int itemCount, LWOOBJID subKey = LWOOBJID_EMPTY, eLootSourceType lootSourceType = eLootSourceType::NONE);
void SendNotifyClientFlagChange(const LWOOBJID& objectID, uint32_t iFlagID, bool bFlag, const SystemAddress& sysAddr);
void SendHelp(const LWOOBJID& objectID, const eHelpType help, const SystemAddress& sysAddr);
void SendChangeObjectWorldState(const LWOOBJID& objectID, eObjectWorldState state, const SystemAddress& sysAddr);
void SendOfferMission(const LWOOBJID& entity, const SystemAddress& sysAddr, int32_t missionID, const LWOOBJID& offererID);
@ -385,7 +387,7 @@ namespace GameMessages {
void SendClientExitTamingMinigame(LWOOBJID objectId, bool bVoluntaryExit, const SystemAddress& sysAddr);
void SendShowPetActionButton(LWOOBJID objectId, int32_t buttonLabel, bool bShow, const SystemAddress& sysAddr);
void SendShowPetActionButton(const LWOOBJID& objectId, const ePetAbilityType petAbility, bool bShow, const SystemAddress& sysAddr);
void SendPlayEmote(LWOOBJID objectId, int32_t emoteID, LWOOBJID target, const SystemAddress& sysAddr);
@ -659,5 +661,3 @@ namespace GameMessages {
void HandleConfirmDonationOnPlayer(RakNet::BitStream* inStream, Entity* entity);
void HandleCancelDonationOnPlayer(RakNet::BitStream* inStream, Entity* entity);
};
#endif // GAMEMESSAGES_H

View File

@ -103,23 +103,13 @@ void PetDigServer::OnDie(Entity* self, Entity* killer) {
}
void PetDigServer::OnUse(Entity* self, Entity* user) {
LOG("Treasure used!");
LOG_DEBUG("Treasure used! LWOOBJID: %d", self->GetObjectID());
auto* petComponent = PetComponent::GetActivePet(user->GetObjectID());
if (!petComponent) return;
if(petComponent->IsReadyToInteract()) { // TODO: Add handling of the "first time" dig message
auto* destroyableComponent = user->GetComponent<DestroyableComponent>();
if (!destroyableComponent) return;
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(petComponent->IsReadyToInteract()) {
petComponent->StartInteractTreasureDig();
imagination -= 1; // TODO: Get rid of this magic number
destroyableComponent->SetImagination(imagination);
Game::entityManager->SerializeEntity(user);
}
}

View File

@ -226,6 +226,10 @@ std::vector<Spawner*> dZoneManager::GetSpawnersInGroup(std::string group) {
return spawnersInGroup;
}
float dZoneManager::GetPetFollowRadius() {
return GetWorldConfig()->petFollowRadius;
}
uint32_t dZoneManager::GetUniqueMissionIdStartingValue() {
if (m_UniqueMissionIdStart == 0) {
auto tableData = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM Missions WHERE isMission = 0 GROUP BY isMission;");

View File

@ -44,6 +44,7 @@ public:
bool GetDisableSaveLocation() { return m_DisableSaveLocation; }
bool GetMountsAllowed() { return m_MountsAllowed; }
bool GetPetsAllowed() { return m_PetsAllowed; }
float GetPetFollowRadius();
uint32_t GetUniqueMissionIdStartingValue();
bool CheckIfAccessibleZone(LWOMAPID zoneID);