clean up PetComponent

This commit is contained in:
jadebenn 2024-04-19 21:53:52 -05:00
parent d88b982904
commit 0b2453241b
8 changed files with 353 additions and 332 deletions

View File

@ -4,6 +4,7 @@ set(DCOMMON_SOURCES
"BinaryIO.cpp" "BinaryIO.cpp"
"dConfig.cpp" "dConfig.cpp"
"Diagnostics.cpp" "Diagnostics.cpp"
"Flag.h"
"Logger.cpp" "Logger.cpp"
"Game.cpp" "Game.cpp"
"GeneralUtils.cpp" "GeneralUtils.cpp"

82
dCommon/Flag.h Normal file
View File

@ -0,0 +1,82 @@
#include "GeneralUtils.h"
#include <bit>
// TODO: Test bounds checking
template <typename T>
requires std::is_enum_v<T>
class Flag {
public:
using type = T;
using underlying_type = std::underlying_type_t<T>;
static constexpr auto MAX_FLAG_VAL = std::bit_width(sizeof(underlying_type));
/**
* Sets one or more flags
* @param flag Flag(s) to set
*/
template <std::same_as<T>... varArg>
constexpr void Set(varArg... flag) noexcept {
m_Flags |= (ConvertFlag(flag) | ...);
}
/**
* Sets ONLY have the specified flag(s), clearing all others
* @param flag Flag(s) to set exclusively
*/
template <std::same_as<T>... varArg>
constexpr void Reset(varArg... flag) {
m_Flags = (ConvertFlag(flag) | ...);
}
/**
* Unsets one or more flags
* @param flag Flag(s) to unset
*/
template <std::same_as<T>... varArg>
constexpr void Unset(varArg... flag) {
m_Flags &= ~(ConvertFlag(flag) | ...);
}
/**
* Returns true all the specified flag(s) are set
* @param flag Flag(s) to check
*/
template <std::same_as<T>... varArg>
constexpr bool Has(varArg... flag) const {
return (m_Flags & (ConvertFlag(flag) | ...)) == (ConvertFlag(flag) | ...);
}
/**
* Returns true ONLY the specified flag(s) are set
* @param flag Flag(s) to check
*/
template <std::same_as<T>... varArg>
constexpr bool HasOnly(varArg... flag) const {
return m_Flags == (ConvertFlag(flag) | ...);
}
/**
* Operator overload to allow for '=' assignment
*/
constexpr Flag& operator=(const T value) {
Reset(value);
return *this;
}
private:
[[nodiscard]]
static constexpr underlying_type ConvertFlag(const T flag) {
auto flag_val = GeneralUtils::ToUnderlying(flag);
if (flag_val != 0) {
return 1 << flag_val;
}
// This should theoeretically be possible to do at compile time, but idk how
if (std::bit_width(flag_val) > MAX_FLAG_VAL) {
throw std::runtime_error{ "Enum value too large to be a flag!" };
}
return flag_val;
}
underlying_type m_Flags;
};

View File

@ -37,7 +37,7 @@
#include "eMissionState.h" #include "eMissionState.h"
#include "dNavMesh.h" #include "dNavMesh.h"
std::unordered_map<LOT, PetComponent::PetPuzzleData> PetComponent::buildCache{}; std::unordered_map<LOT, PetComponent::PuzzleData> PetComponent::buildCache{};
std::unordered_map<LWOOBJID, LWOOBJID> PetComponent::currentActivities{}; std::unordered_map<LWOOBJID, LWOOBJID> PetComponent::currentActivities{};
std::unordered_map<LWOOBJID, LWOOBJID> PetComponent::activePets{}; std::unordered_map<LWOOBJID, LWOOBJID> PetComponent::activePets{};
@ -45,7 +45,7 @@ std::unordered_map<LWOOBJID, LWOOBJID> PetComponent::activePets{};
* Maps all the pet lots to a flag indicating that the player has caught it. All basic pets have been guessed by ObjID * Maps all the pet lots to a flag indicating that the player has caught it. All basic pets have been guessed by ObjID
* while the faction ones could be checked using their respective missions. * while the faction ones could be checked using their respective missions.
*/ */
std::map<LOT, int32_t> PetComponent::petFlags = { std::map<LOT, int32_t> PetComponent::petFlags{
{ 3050, 801 }, // Elephant { 3050, 801 }, // Elephant
{ 3054, 803 }, // Cat { 3054, 803 }, // Cat
{ 3195, 806 }, // Triceratops { 3195, 806 }, // Triceratops
@ -78,31 +78,17 @@ std::map<LOT, int32_t> PetComponent::petFlags = {
PetComponent::PetComponent(Entity* parentEntity, uint32_t componentId) : Component{ parentEntity } { PetComponent::PetComponent(Entity* parentEntity, uint32_t componentId) : Component{ parentEntity } {
m_PetInfo = CDClientManager::GetTable<CDPetComponentTable>()->GetByID(componentId); // TODO: Make reference when safe m_PetInfo = CDClientManager::GetTable<CDPetComponentTable>()->GetByID(componentId); // TODO: Make reference when safe
m_ComponentId = componentId; m_ComponentId = componentId;
m_Interaction = LWOOBJID_EMPTY;
m_InteractType = PetInteractType::none;
m_Owner = LWOOBJID_EMPTY;
m_ModerationStatus = 0;
m_Tamer = LWOOBJID_EMPTY;
m_ModelId = LWOOBJID_EMPTY;
m_Timer = 0;
m_TimerAway = 0;
m_TimerBounce = 0;
m_DatabaseId = LWOOBJID_EMPTY;
m_Flags = PetFlag::SPAWNING; // Tameable m_Flags = PetFlag::SPAWNING; // Tameable
m_Ability = ePetAbilityType::Invalid;
m_StartPosition = m_Parent->GetPosition(); m_StartPosition = m_Parent->GetPosition();
m_MovementAI = nullptr; m_MovementAI = nullptr;
m_Preconditions = nullptr;
m_ReadyToInteract = false; m_ReadyToInteract = false;
SetPetAiState(PetAiState::spawn); m_State = PetAiState::spawn;
SetIsHandlingInteraction(false); SetIsHandlingInteraction(false);
std::string checkPreconditions = GeneralUtils::UTF16ToWTF8(parentEntity->GetVar<std::u16string>(u"CheckPrecondition")); std::string checkPreconditions = GeneralUtils::UTF16ToWTF8(parentEntity->GetVar<std::u16string>(u"CheckPrecondition"));
m_Preconditions =
if (!checkPreconditions.empty()) { checkPreconditions.empty() ? std::nullopt : std::make_optional(PreconditionExpression(checkPreconditions));
SetPreconditions(checkPreconditions);
}
m_FollowRadius = 8.0f; //Game::zoneManager->GetPetFollowRadius(); // TODO: FIX THIS TO LOAD DYNAMICALLY m_FollowRadius = 8.0f; //Game::zoneManager->GetPetFollowRadius(); // TODO: FIX THIS TO LOAD DYNAMICALLY
} }
@ -113,13 +99,13 @@ void PetComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpd
outBitStream.Write1(); // Always serialize as dirty for now outBitStream.Write1(); // Always serialize as dirty for now
outBitStream.Write(m_Flags); outBitStream.Write(m_Flags);
outBitStream.Write(tamed ? m_Ability : ePetAbilityType::Invalid); // Something with the overhead icon? outBitStream.Write(tamed ? m_Interaction.ability : ePetAbilityType::Invalid); // Something with the overhead icon?
const bool interacting = m_Interaction != LWOOBJID_EMPTY; const bool interacting = m_Interaction.obj != LWOOBJID_EMPTY;
outBitStream.Write(interacting); outBitStream.Write(interacting);
if (interacting) { if (interacting) {
outBitStream.Write(m_Interaction); outBitStream.Write(m_Interaction.obj);
} }
outBitStream.Write(tamed); outBitStream.Write(tamed);
@ -148,16 +134,11 @@ void PetComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpd
} }
} }
void PetComponent::SetPetAiState(PetAiState newState) {
if (newState == GetPetAiState()) return;
this->m_State = newState;
}
void PetComponent::OnUse(Entity* originator) { void PetComponent::OnUse(Entity* originator) {
LOG_DEBUG("PET USE!"); LOG_DEBUG("PET USE!");
if (IsReadyToInteract()) { if (IsReadyToInteract()) {
switch (GetAbility()) { switch (m_Interaction.ability) {
case ePetAbilityType::DigAtPosition: // Treasure dig case ePetAbilityType::DigAtPosition: // Treasure dig
StartInteractTreasureDig(); StartInteractTreasureDig();
break; break;
@ -186,7 +167,7 @@ void PetComponent::OnUse(Entity* originator) {
if (!inventoryComponent) return; if (!inventoryComponent) return;
if (m_Preconditions != nullptr && !m_Preconditions->Check(originator, true)) return; if (m_Preconditions.has_value() && !m_Preconditions->Check(originator, true)) return;
auto* const movementAIComponent = m_Parent->GetComponent<MovementAIComponent>(); auto* const movementAIComponent = m_Parent->GetComponent<MovementAIComponent>();
@ -223,7 +204,7 @@ void PetComponent::OnUse(Entity* originator) {
buildFile = std::string(result.getStringField("ValidPiecesLXF")); buildFile = std::string(result.getStringField("ValidPiecesLXF"));
PetPuzzleData data; PuzzleData data;
data.buildFile = buildFile; data.buildFile = buildFile;
data.puzzleModelLot = result.getIntField("PuzzleModelLot"); data.puzzleModelLot = result.getIntField("PuzzleModelLot");
data.timeLimit = result.getFloatField("Timelimit"); data.timeLimit = result.getFloatField("Timelimit");
@ -324,7 +305,7 @@ void PetComponent::OnUse(Entity* originator) {
GameMessages::SendNotifyPetTamingPuzzleSelected(originator->GetObjectID(), bricks, originator->GetSystemAddress()); GameMessages::SendNotifyPetTamingPuzzleSelected(originator->GetObjectID(), bricks, originator->GetSystemAddress());
m_Tamer = originator->GetObjectID(); m_Tamer = originator->GetObjectID();
SetFlag(PetFlag::IDLE, PetFlag::UNKNOWN4); m_Flags.Set(PetFlag::IDLE, PetFlag::UNKNOWN4);
currentActivities.insert_or_assign(m_Tamer, m_Parent->GetObjectID()); currentActivities.insert_or_assign(m_Tamer, m_Parent->GetObjectID());
@ -352,7 +333,7 @@ void PetComponent::Update(float deltaTime) {
ClientFailTamingMinigame(); // TODO: This is not despawning the built model correctly ClientFailTamingMinigame(); // TODO: This is not despawning the built model correctly
} }
if (HasFlag(PetFlag::SPAWNING)) OnSpawn(); if (m_Flags.Has(PetFlag::SPAWNING)) OnSpawn();
// Handle pet AI states // Handle pet AI states
switch (m_State) { switch (m_State) {
@ -519,7 +500,7 @@ void PetComponent::NotifyTamingBuildSuccess(const NiPoint3 position) {
missionComponent->Progress(eMissionTaskType::PET_TAMING, m_Parent->GetLOT()); missionComponent->Progress(eMissionTaskType::PET_TAMING, m_Parent->GetLOT());
} }
SetOnlyFlag(PetFlag::IDLE); m_Flags.Reset(PetFlag::IDLE);
auto* const characterComponent = tamer->GetComponent<CharacterComponent>(); auto* const characterComponent = tamer->GetComponent<CharacterComponent>();
if (characterComponent != nullptr) { if (characterComponent != nullptr) {
@ -630,7 +611,7 @@ void PetComponent::ClientExitTamingMinigame(bool voluntaryExit) {
currentActivities.erase(m_Tamer); currentActivities.erase(m_Tamer);
SetOnlyFlag(PetFlag::TAMEABLE); m_Flags.Reset(PetFlag::TAMEABLE);
m_Tamer = LWOOBJID_EMPTY; m_Tamer = LWOOBJID_EMPTY;
m_Timer = 0; m_Timer = 0;
@ -679,7 +660,7 @@ void PetComponent::ClientFailTamingMinigame() {
currentActivities.erase(m_Tamer); currentActivities.erase(m_Tamer);
SetOnlyFlag(PetFlag::TAMEABLE); m_Flags.Reset(PetFlag::TAMEABLE);
m_Tamer = LWOOBJID_EMPTY; m_Tamer = LWOOBJID_EMPTY;
m_Timer = 0; m_Timer = 0;
@ -742,12 +723,12 @@ void PetComponent::OnSpawn() {
//SetOnlyFlag(IDLE); //SetStatus(PetFlag::NONE); //SetOnlyFlag(IDLE); //SetStatus(PetFlag::NONE);
SetPetAiState(PetAiState::follow); SetPetAiState(PetAiState::follow);
} else { } else {
SetFlag(PetFlag::TAMEABLE); m_Flags.Set(PetFlag::TAMEABLE);
SetPetAiState(PetAiState::idle); SetPetAiState(PetAiState::idle);
} }
SetFlag(PetFlag::IDLE); m_Flags.Set(PetFlag::IDLE);
UnsetFlag(PetFlag::SPAWNING); m_Flags.Unset(PetFlag::SPAWNING);
Game::entityManager->SerializeEntity(m_Parent); Game::entityManager->SerializeEntity(m_Parent);
} }
@ -825,7 +806,7 @@ void PetComponent::OnInteract() {
return; return;
} }
switch (GetInteractType()) { switch (m_Interaction.type) {
case PetInteractType::bouncer: case PetInteractType::bouncer:
if (IsReadyToInteract()) HandleInteractBouncer(); if (IsReadyToInteract()) HandleInteractBouncer();
else SetupInteractBouncer(); else SetupInteractBouncer();
@ -844,10 +825,10 @@ void PetComponent::OnInteract() {
} }
} }
void PetComponent::StartInteract(const NiPoint3& position, const PetInteractType interactType, const LWOOBJID& interactID) { void PetComponent::StartInteract(const NiPoint3& position, const PetInteractType interactionType, const LWOOBJID& interactID) {
SetInteraction(interactID); // TODO: Check if this should be serialized for goToObj m_Interaction.obj; // TODO: Check if this should be serialized for goToObj
SetInteractType(interactType); m_Interaction.type = interactionType;
SetAbility(ePetAbilityType::GoToObject); m_Interaction.ability = 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);
@ -860,13 +841,13 @@ void PetComponent::StopInteract(bool bDontSerialize) {
Entity* const owner = GetOwner(); Entity* const owner = GetOwner();
if (!owner) return; if (!owner) return;
const auto petAbility = ePetAbilityType::Invalid; constexpr auto petAbility = ePetAbilityType::Invalid;
SetInteraction(LWOOBJID_EMPTY); m_Interaction.obj = LWOOBJID_EMPTY;
SetInteractType(PetInteractType::none); m_Interaction.type = PetInteractType::none;
SetAbility(petAbility); m_Interaction.ability = petAbility;
SetPetAiState(PetAiState::follow); SetPetAiState(PetAiState::follow);
SetOnlyFlag(PetFlag::IDLE); m_Flags.Reset(PetFlag::IDLE);
SetIsReadyToInteract(false); SetIsReadyToInteract(false);
SetIsHandlingInteraction(false); // Needed? SetIsHandlingInteraction(false); // Needed?
m_MovementAI->SetMaxSpeed(m_PetInfo.sprintSpeed); m_MovementAI->SetMaxSpeed(m_PetInfo.sprintSpeed);
@ -886,11 +867,11 @@ void PetComponent::SetupInteractBouncer() {
LOG_DEBUG("Setting up bouncer interaction!"); LOG_DEBUG("Setting up bouncer interaction!");
SetIsReadyToInteract(true); SetIsReadyToInteract(true);
const auto petAbility = ePetAbilityType::JumpOnObject; constexpr auto petAbility = ePetAbilityType::JumpOnObject;
SetAbility(petAbility); m_Interaction.ability = petAbility;
UnsetFlag(PetFlag::IDLE); m_Flags.Unset(PetFlag::IDLE);
SetFlag(PetFlag::ON_SWITCH, PetFlag::NOT_WAITING); //SetStatus(PetFlag::NOT_WAITING); // TODO: Double-check this is the right flag being set m_Flags.Set(PetFlag::ON_SWITCH, PetFlag::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
@ -969,11 +950,11 @@ void PetComponent::SetupInteractTreasureDig() {
LOG_DEBUG("Setting up dig interaction!"); LOG_DEBUG("Setting up dig interaction!");
SetIsReadyToInteract(true); SetIsReadyToInteract(true);
const auto petAbility = ePetAbilityType::DigAtPosition; constexpr auto petAbility = ePetAbilityType::DigAtPosition;
SetAbility(petAbility); m_Interaction.ability = petAbility;
UnsetFlag(PetFlag::IDLE); m_Flags.Unset(PetFlag::IDLE);
SetFlag(PetFlag::ON_SWITCH, PetFlag::NOT_WAITING); // TODO: Double-check this is the right flag being set m_Flags.Set(PetFlag::ON_SWITCH, 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
@ -1007,8 +988,8 @@ void PetComponent::StartInteractTreasureDig() {
Game::entityManager->SerializeEntity(user); Game::entityManager->SerializeEntity(user);
SetIsHandlingInteraction(true); SetIsHandlingInteraction(true);
UnsetFlag(PetFlag::ON_SWITCH, PetFlag::NOT_WAITING); // TODO: FIND THE CORRECT STATUS TO USE HERE m_Flags.Unset(PetFlag::ON_SWITCH, PetFlag::NOT_WAITING); // TODO: FIND THE CORRECT STATUS TO USE HERE
SetFlag(PetFlag::IDLE); m_Flags.Set(PetFlag::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);
@ -1057,7 +1038,7 @@ void PetComponent::Activate(Item* item, bool registerPet, bool fromTaming) { //
auto* const owner = GetOwner(); auto* const owner = GetOwner();
if (!owner) return; if (!owner) return;
SetFlag(PetFlag::SPAWNING); m_Flags.Set(PetFlag::SPAWNING);
auto databaseData = inventoryComponent->GetDatabasePet(m_DatabaseId); auto databaseData = inventoryComponent->GetDatabasePet(m_DatabaseId);
@ -1203,39 +1184,11 @@ void PetComponent::Command(const NiPoint3& position, const LWOOBJID source, cons
} }
} }
LWOOBJID PetComponent::GetOwnerId() const {
return m_Owner;
}
Entity* PetComponent::GetOwner() const { Entity* PetComponent::GetOwner() const {
return Game::entityManager->GetEntity(m_Owner); return Game::entityManager->GetEntity(m_Owner);
} }
LWOOBJID PetComponent::GetDatabaseId() const { PetComponent* PetComponent::GetTamingPet(const LWOOBJID tamer) {
return m_DatabaseId;
}
LWOOBJID PetComponent::GetInteraction() const {
return m_Interaction;
}
LWOOBJID PetComponent::GetItemId() const {
return m_ItemId;
}
ePetAbilityType PetComponent::GetAbility() const {
return m_Ability;
}
void PetComponent::SetInteraction(LWOOBJID value) {
m_Interaction = value;
}
void PetComponent::SetAbility(ePetAbilityType value) {
m_Ability = value;
}
PetComponent* PetComponent::GetTamingPet(LWOOBJID tamer) {
const auto& pair = currentActivities.find(tamer); const auto& pair = currentActivities.find(tamer);
if (pair == currentActivities.end()) { if (pair == currentActivities.end()) {
@ -1253,7 +1206,7 @@ PetComponent* PetComponent::GetTamingPet(LWOOBJID tamer) {
return entity->GetComponent<PetComponent>(); return entity->GetComponent<PetComponent>();
} }
PetComponent* PetComponent::GetActivePet(LWOOBJID owner) { PetComponent* PetComponent::GetActivePet(const LWOOBJID owner) {
const auto& pair = activePets.find(owner); const auto& pair = activePets.find(owner);
if (pair == activePets.end()) { if (pair == activePets.end()) {
@ -1271,13 +1224,6 @@ PetComponent* PetComponent::GetActivePet(LWOOBJID owner) {
return entity->GetComponent<PetComponent>(); return entity->GetComponent<PetComponent>();
} }
Entity* PetComponent::GetParentEntity() const {
return m_Parent;
}
PetComponent::~PetComponent() {
}
void PetComponent::SetPetNameForModeration(const std::string& petName) { void PetComponent::SetPetNameForModeration(const std::string& petName) {
int approved = 1; //default, in mod int approved = 1; //default, in mod
@ -1291,7 +1237,7 @@ void PetComponent::SetPetNameForModeration(const std::string& petName) {
} }
void PetComponent::LoadPetNameFromModeration() { void PetComponent::LoadPetNameFromModeration() {
auto petNameInfo = Database::Get()->GetPetNameInfo(m_DatabaseId); const auto petNameInfo = Database::Get()->GetPetNameInfo(m_DatabaseId);
if (petNameInfo) { if (petNameInfo) {
m_ModerationStatus = petNameInfo->approvalStatus; m_ModerationStatus = petNameInfo->approvalStatus;
if (m_ModerationStatus == 2) { if (m_ModerationStatus == 2) {
@ -1299,7 +1245,3 @@ void PetComponent::LoadPetNameFromModeration() {
} }
} }
} }
void PetComponent::SetPreconditions(std::string& preconditions) {
m_Preconditions = new PreconditionExpression(preconditions);
}

View File

@ -2,6 +2,7 @@
#define PETCOMPONENT_H #define PETCOMPONENT_H
#include "Entity.h" #include "Entity.h"
#include "Flag.h"
#include "MovementAIComponent.h" #include "MovementAIComponent.h"
#include "Component.h" #include "Component.h"
#include "Preconditions.h" #include "Preconditions.h"
@ -11,6 +12,13 @@
#include "CDPetComponentTable.h" #include "CDPetComponentTable.h"
#include "CDClientManager.h" #include "CDClientManager.h"
#include <optional>
#include <gtest/gtest.h>
// Forward declarations
class PetTest;
class PetComponentFlagTest;
/* /*
* The current state of the pet AI * The current state of the pet AI
*/ */
@ -37,44 +45,18 @@ enum class PetInteractType : uint8_t {
*/ */
enum class PetFlag : uint32_t { enum class PetFlag : uint32_t {
NONE, NONE,
IDLE = 1 << 0, //0x01 - Seems to be "idle," which the game doesn't differentiate from "follow" IDLE, //0x01 - Seems to be "idle," which the game doesn't differentiate from "follow"
UNKNOWN2 = 1 << 1, //0x02, UNKNOWN2, //0x02,
UNKNOWN4 = 1 << 2, //0x04 - FOLLOWING(?) UNKNOWN4, //0x04 - FOLLOWING(?)
BEING_TAMED = 1 << 4, //0x10, BEING_TAMED, //0x10,
NOT_WAITING = 1 << 5, //0x20, NOT_WAITING, //0x20,
IMMOBILE = 1 << 6, //0x40 - Seems to be the "stop moving" flag - called when taming begins and stays active until a name is submitted IMMOBILE, //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, //0x80
ON_SWITCH = 1 << 8, //0x100 ON_SWITCH, //0x100
UNKNOWN1024 = 1 << 10, //0x400 UNKNOWN1024 = 10, //0x400
TAMEABLE = 1 << 26 //0x4000000 TAMEABLE = 26 //0x4000000
}; };
/**
* Define bitwise operators for PetFlag (TODO: Encapsulate into proper class)
*/
constexpr PetFlag operator|(const PetFlag lhs, const PetFlag rhs) {
using underlying_type = std::underlying_type_t<PetFlag>;
return static_cast<PetFlag>(static_cast<underlying_type>(lhs) | static_cast<underlying_type>(rhs));
}
constexpr PetFlag& operator|=(PetFlag& lhs, const PetFlag rhs) {
return lhs = lhs | rhs;
}
constexpr PetFlag operator&(const PetFlag lhs, const PetFlag rhs) {
using underlying_type = std::underlying_type_t<PetFlag>;
return static_cast<PetFlag>(static_cast<underlying_type>(lhs) & static_cast<underlying_type>(rhs));
}
constexpr PetFlag& operator&=(PetFlag& lhs, const PetFlag rhs) {
return lhs = lhs & rhs;
}
constexpr PetFlag operator~(const PetFlag flag) {
using underlying_type = std::underlying_type_t<PetFlag>;
return static_cast<PetFlag>(~static_cast<underlying_type>(flag));
}
/** /**
* The pet emote animation ids that can used in PetComponent::Command() * The pet emote animation ids that can used in PetComponent::Command()
*/ */
@ -88,8 +70,7 @@ enum class PetEmote : int32_t {
* Represents an entity that is a pet. This pet can be tamed and consequently follows the tamer around, allowing it * 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. * to dig for treasure and activate pet bouncers.
*/ */
class PetComponent final : public Component class PetComponent final : public Component {
{
public: public:
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::PET; static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::PET;
@ -100,7 +81,7 @@ public:
*/ */
explicit PetComponent(Entity* parentEntity, uint32_t componentId); explicit PetComponent(Entity* parentEntity, uint32_t componentId);
~PetComponent() override; ~PetComponent() override = default;
/** /**
* Serializes the pet * Serializes the pet
@ -113,56 +94,16 @@ public:
* Sets the AI state of the pet * Sets the AI state of the pet
* @param newState New pet AI state * @param newState New pet AI state
*/ */
void SetPetAiState(PetAiState newState); void SetPetAiState(const PetAiState newState) noexcept {
m_State = newState;
};
/** /**
* Gets the AI state of the pet * Gets the AI state of the pet
*/ */
PetAiState GetPetAiState() const { return m_State; } [[nodiscard]]
PetAiState GetPetAiState() const noexcept {
/** return m_State;
* Sets one or more pet flags
* @param flag PetFlag(s) to set
*/
template <typename... varArg>
void SetFlag(varArg... flag) {
m_Flags |= (flag | ...);
}
/**
* Sets the pet to ONLY have the specified flag(s), clearing all others
* @param flag PetFlag(s) to set exclusively
*/
template <typename... varArg>
void SetOnlyFlag(varArg... flag) {
m_Flags = (flag | ...);
}
/**
* Unsets one or more pet flags
* @param flag PetFlag(s) to unset
*/
template <typename... varArg>
void UnsetFlag(varArg... flag) {
m_Flags &= ~(flag | ...);
}
/**
* Returns true if the pet has all the specified flag(s)
* @param flag PetFlag(s) to check
*/
template <typename... varArg>
bool HasFlag(varArg... flag) const {
return (m_Flags & (flag | ...)) == (flag | ...);
}
/**
* Returns true if the pet has ONLY the specified flag(s)
* @param flag PetFlag(s) to check if the pet has exclusively
*/
template <typename... varArg>
bool HasOnlyFlag(varArg... flag) const {
return m_Flags == (flag | ...);
} }
/** /**
@ -238,7 +179,7 @@ public:
/** /**
* Start a pet interaction with an object at a given position * Start a pet interaction with an object at a given position
*/ */
void StartInteract(const NiPoint3& position, const PetInteractType interactType, const LWOOBJID& interactID); void StartInteract(const NiPoint3& position, const PetInteractType interactionType, const LWOOBJID& interactID);
/** /**
* Stop a pet interaction with an object * Stop a pet interaction with an object
@ -246,16 +187,6 @@ public:
*/ */
void StopInteract(bool bDontSerialize = false); void StopInteract(bool bDontSerialize = false);
/**
* Set the type of interaction the pet is executing
*/
void SetInteractType(PetInteractType interactType) { m_InteractType = interactType; };
/**
* Get the type of interaction the pet is executing
*/
PetInteractType GetInteractType() { return m_InteractType; };
/** /**
* Spawns a pet from an item in the inventory of an owner * Spawns a pet from an item in the inventory of an owner
* @param item the item to create the pet from * @param item the item to create the pet from
@ -287,90 +218,75 @@ public:
* Returns the ID of the owner of this pet (if any) * Returns the ID of the owner of this pet (if any)
* @return the ID of the owner of this pet * @return the ID of the owner of this pet
*/ */
LWOOBJID GetOwnerId() const; [[nodiscard]]
LWOOBJID GetOwnerId() const noexcept {
return m_Owner;
};
/** /**
* Returns the entity that owns this pet (if any) * Returns the entity that owns this pet (if any)
* @return the entity that owns this pet * @return the entity that owns this pet
*/ */
[[nodiscard]]
Entity* GetOwner() const; Entity* GetOwner() const;
/** /**
* Returns the ID that is stored in the database with regards to this pet, only set for pets that are tamed * Returns the ID that is stored in the database with regards to this pet, only set for pets that are tamed
* @return the ID that is stored in the database with regards to this pet * @return the ID that is stored in the database with regards to this pet
*/ */
LWOOBJID GetDatabaseId() const; [[nodiscard]]
LWOOBJID GetDatabaseId() const noexcept {
/** return m_DatabaseId;
* Returns the ID of the object that the pet is currently interacting with, could be a treasure chest or a switch }
* @return the ID of the object that the pet is currently interacting with
*/
LWOOBJID GetInteraction() const;
/**
* Sets the ID that the pet is interacting with
* @param value the ID that the pet is interacting with
*/
void SetInteraction(LWOOBJID value);
/** /**
* Returns the ID that this pet was spawned from, only set for tamed pets * Returns the ID that this pet was spawned from, only set for tamed pets
* @return the ID that this pet was spawned from * @return the ID that this pet was spawned from
*/ */
LWOOBJID GetItemId() const; [[nodiscard]]
LWOOBJID GetItemId() const noexcept {
/** return m_ItemId;
* Returns the status of this pet, e.g. tamable or tamed. The values here are still a bit of mystery and likely a }
* bit map
* @return the status of this pet
*/
/*uint32_t GetStatus() const;*/
/**
* Sets the current status of the pet
* @param value the current status of the pet to set
*/
/*void SetStatus(uint32_t value);*/
/**
* Returns an ability the pet may perform, currently unused
* @return an ability the pet may perform
*/
ePetAbilityType GetAbility() const;
/**
* Sets the ability of the pet, currently unused
* @param value the ability to set
*/
void SetAbility(ePetAbilityType value);
/** /**
* Sets preconditions for the pet that need to be met before it can be tamed * Sets preconditions for the pet that need to be met before it can be tamed
* @param conditions the preconditions to set * @param conditions the preconditions to set
*/ */
void SetPreconditions(std::string& conditions); void SetPreconditions(const std::string& preconditions) {
m_Preconditions = PreconditionExpression(preconditions);
}
/** /**
* Sets if the pet is ready to interact with an object * Sets if the pet is ready to interact with an object
* @param isReady whether the pet is ready to interact (true) or not (false) * @param isReady whether the pet is ready to interact (true) or not (false)
*/ */
void SetIsReadyToInteract(bool isReady) { m_ReadyToInteract = isReady; }; void SetIsReadyToInteract(const bool isReady) {
m_ReadyToInteract = isReady;
};
/** /**
* @return is pet ready to interact with an object * @return is pet ready to interact with an object
*/ */
bool IsReadyToInteract() { return m_ReadyToInteract; }; [[nodiscard]]
bool IsReadyToInteract() const noexcept {
return m_ReadyToInteract;
}
/** /**
* Sets if the pet is currently handling an interaction with an object * 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 * @param isHandlingInteraction whether the pet is currently handling an interaction with an object
*/ */
void SetIsHandlingInteraction(bool isHandlingInteraction) { m_IsHandlingInteraction = isHandlingInteraction; }; void SetIsHandlingInteraction(const bool isHandlingInteraction) {
m_IsHandlingInteraction = isHandlingInteraction;
}
/** /**
* @return is pet currently handling an interaction with an object * @return is pet currently handling an interaction with an object
*/ */
bool IsHandlingInteraction() { return m_IsHandlingInteraction; }; [[nodiscard]]
bool IsHandlingInteraction() const noexcept {
return m_IsHandlingInteraction;
};
/** /**
* Set up the pet bouncer interaction * Set up the pet bouncer interaction
@ -406,7 +322,10 @@ public:
* Returns the entity that this component belongs to * Returns the entity that this component belongs to
* @return the entity that this component belongs to * @return the entity that this component belongs to
*/ */
Entity* GetParentEntity() const; [[nodiscard]]
Entity* GetParentEntity() const noexcept {
return m_Parent;
}
/** /**
* Sets the name of the pet to be moderated * Sets the name of the pet to be moderated
@ -425,6 +344,7 @@ public:
* @param tamer the entity that's currently taming * @param tamer the entity that's currently taming
* @return the pet component of the entity that's being tamed * @return the pet component of the entity that's being tamed
*/ */
[[nodiscard]]
static PetComponent* GetTamingPet(LWOOBJID tamer); static PetComponent* GetTamingPet(LWOOBJID tamer);
/** /**
@ -432,6 +352,7 @@ public:
* @param owner the owner of the pet that's spawned * @param owner the owner of the pet that's spawned
* @return the pet component of the entity that was spawned by the owner * @return the pet component of the entity that was spawned by the owner
*/ */
[[nodiscard]]
static PetComponent* GetActivePet(LWOOBJID owner); static PetComponent* GetActivePet(LWOOBJID owner);
/** /**
@ -443,11 +364,15 @@ public:
void AddDrainImaginationTimer(Item* item, bool fromTaming = false); void AddDrainImaginationTimer(Item* item, bool fromTaming = false);
private: private:
// Needed so these can access flags
friend class DamagingPets;
friend class PetTest;
FRIEND_TEST(PetTest, PetComponentFlagTest);
/** /**
* Information for the minigame to be completed * Information for the minigame to be completed
*/ */
struct PetPuzzleData struct PuzzleData
{ {
/** /**
* The LOT of the object that is to be created * The LOT of the object that is to be created
@ -475,6 +400,28 @@ private:
int32_t numValidPieces; int32_t numValidPieces;
}; };
struct Interaction {
/**
* The type of object that the pet is currently interacting with (e.g. a treasure chest or switch)
*/
PetInteractType type = PetInteractType::none;
/**
* The interaction ability
*/
ePetAbilityType ability = ePetAbilityType::Invalid;
/**
* The ID of the object that the pet is currently interacting with (e.g. a treasure chest or switch)
*/
LWOOBJID obj = LWOOBJID_EMPTY;
};
/**
* Pet interaction info
*/
Interaction m_Interaction{};
/** /**
* Cache of all the pets that are currently spawned, indexed by tamer * Cache of all the pets that are currently spawned, indexed by tamer
*/ */
@ -488,7 +435,7 @@ private:
/** /**
* Cache of all the minigames and their information from the database * Cache of all the minigames and their information from the database
*/ */
static std::unordered_map<LOT, PetComponent::PetPuzzleData> buildCache; static std::unordered_map<LOT, PetComponent::PuzzleData> buildCache;
/** /**
* Flags that indicate that a player has tamed a pet, indexed by the LOT of the pet * Flags that indicate that a player has tamed a pet, indexed by the LOT of the pet
@ -508,32 +455,22 @@ private:
/** /**
* The ID of the model that was built to complete the taming minigame for this pet * The ID of the model that was built to complete the taming minigame for this pet
*/ */
LWOOBJID m_ModelId; LWOOBJID m_ModelId = LWOOBJID_EMPTY;
/**
* The ID of the object that the pet is currently interacting with (e.g. a treasure chest or switch)
*/
LWOOBJID m_Interaction;
/**
* The type of object that the pet is currently interacting with (e.g. a treasure chest or switch)
*/
PetInteractType m_InteractType;
/** /**
* The ID of the entity that owns this pet * The ID of the entity that owns this pet
*/ */
LWOOBJID m_Owner; LWOOBJID m_Owner = LWOOBJID_EMPTY;
/** /**
* The ID of the entity that is currently taming this pet * The ID of the entity that is currently taming this pet
*/ */
LWOOBJID m_Tamer; LWOOBJID m_Tamer = LWOOBJID_EMPTY;
/** /**
* The ID under which this pet is stored in the database (if it's tamed) * The ID under which this pet is stored in the database (if it's tamed)
*/ */
LWOOBJID m_DatabaseId; LWOOBJID m_DatabaseId = LWOOBJID_EMPTY;
/** /**
* The ID of the item from which this pet was created * The ID of the item from which this pet was created
@ -543,7 +480,7 @@ private:
/** /**
* The moderation status for the name of this pet * The moderation status for the name of this pet
*/ */
uint32_t m_ModerationStatus; uint32_t m_ModerationStatus = 0;
/** /**
* The name of this pet * The name of this pet
@ -558,32 +495,27 @@ private:
/** /**
* The current flags of the pet (e.g. tamable, tamed, etc). * The current flags of the pet (e.g. tamable, tamed, etc).
*/ */
PetFlag m_Flags; Flag<PetFlag> m_Flags;
/** /**
* The current state of the pet AI * The current state of the pet AI
*/ */
PetAiState m_State; PetAiState m_State;
/**
* A currently active ability, mostly unused
*/
ePetAbilityType m_Ability;
/** /**
* The time an entity has left to complete the minigame * The time an entity has left to complete the minigame
*/ */
float m_Timer; float m_Timer = 0;
/** /**
* A timer that tracks how long a tamed pet has been to far away from its owner, triggering a teleport after timeout * A timer that tracks how long a tamed pet has been to far away from its owner, triggering a teleport after timeout
*/ */
float m_TimerAway; float m_TimerAway = 0;
/** /**
* A timer that tracks how long until a tamed pet will bounce again when standing over a treasure dig site * A timer that tracks how long until a tamed pet will bounce again when standing over a treasure dig site
*/ */
float m_TimerBounce; float m_TimerBounce = 0;
/** /**
* Boolean that sets if a pet is ready to interact with an object * Boolean that sets if a pet is ready to interact with an object
@ -608,7 +540,7 @@ private:
/** /**
* Preconditions that need to be met before an entity can tame this pet * Preconditions that need to be met before an entity can tame this pet
*/ */
PreconditionExpression* m_Preconditions; std::optional<PreconditionExpression> m_Preconditions;
/** /**
* Pet information loaded from the CDClientDatabase * Pet information loaded from the CDClientDatabase

View File

@ -77,10 +77,10 @@ void DamagingPets::MakeUntamable(Entity* self) {
auto* const petComponent = self->GetComponent<PetComponent>(); auto* const petComponent = self->GetComponent<PetComponent>();
// If the pet is currently not being tamed, make it hostile // If the pet is currently not being tamed, make it hostile
if (petComponent != nullptr && !petComponent->HasFlag(PetFlag::NOT_WAITING)) { if (petComponent != nullptr && !petComponent->m_Flags.Has(PetFlag::NOT_WAITING)) {
self->SetNetworkVar<bool>(u"bIAmTamable", false); self->SetNetworkVar<bool>(u"bIAmTamable", false);
self->SetVar<bool>(u"IsEvil", true); self->SetVar<bool>(u"IsEvil", true);
petComponent->SetFlag(PetFlag::IDLE); petComponent->m_Flags.Set(PetFlag::IDLE);
auto* combatAIComponent = self->GetComponent<BaseCombatAIComponent>(); auto* combatAIComponent = self->GetComponent<BaseCombatAIComponent>();
if (combatAIComponent != nullptr) { if (combatAIComponent != nullptr) {
@ -110,7 +110,7 @@ void DamagingPets::ClearEffects(Entity* self) {
auto* petComponent = self->GetComponent<PetComponent>(); auto* petComponent = self->GetComponent<PetComponent>();
if (petComponent != nullptr) { if (petComponent != nullptr) {
petComponent->SetFlag(PetFlag::TAMEABLE, PetFlag::UNKNOWN2); petComponent->m_Flags.Set(PetFlag::TAMEABLE, PetFlag::UNKNOWN2);
} }
auto* combatAIComponent = self->GetComponent<BaseCombatAIComponent>(); auto* combatAIComponent = self->GetComponent<BaseCombatAIComponent>();

View File

@ -1,6 +1,7 @@
set(DCOMMONTEST_SOURCES set(DCOMMONTEST_SOURCES
"AMFDeserializeTests.cpp" "AMFDeserializeTests.cpp"
"Amf3Tests.cpp" "Amf3Tests.cpp"
"FlagTests.cpp"
"ToUnderlyingTests.cpp" "ToUnderlyingTests.cpp"
"HeaderSkipTest.cpp" "HeaderSkipTest.cpp"
"TestCDFeatureGatingTable.cpp" "TestCDFeatureGatingTable.cpp"

View File

@ -0,0 +1,67 @@
#include <gtest/gtest.h>
#include "Flag.h"
enum class TestFlag : uint8_t {
FLAG0,
FLAG1,
FLAG2,
FLAG3,
FLAG4
};
/**
* Test bitset flags
*/
TEST(FlagTests, FlagMethodTest) {
using enum TestFlag;
auto flag = Flag<TestFlag>{};
// Test setting and reading single flags, exclusively
flag.Reset(FLAG0);
ASSERT_TRUE(flag.HasOnly(FLAG0));
flag.Reset(FLAG2);
ASSERT_TRUE(flag.HasOnly(FLAG2));
ASSERT_FALSE(flag.HasOnly(FLAG1));
// Test setting and reading multiple flags, exclusively
flag.Reset(FLAG3, FLAG1);
ASSERT_FALSE(flag.Has(FLAG2));
ASSERT_TRUE(flag.Has(FLAG3));
ASSERT_TRUE(flag.Has(FLAG1));
ASSERT_TRUE(flag.Has(FLAG3, FLAG1));
ASSERT_FALSE(flag.HasOnly(FLAG3));
ASSERT_FALSE(flag.HasOnly(FLAG1));
ASSERT_TRUE(flag.HasOnly(FLAG3, FLAG1));
// Test flags are being properly reset for next batch of tests
flag.Reset(FLAG0);
ASSERT_TRUE(flag.Has(FLAG0));
ASSERT_TRUE(flag.HasOnly(FLAG0));
ASSERT_FALSE(flag.Has(FLAG3));
ASSERT_FALSE(flag.Has(FLAG3, FLAG1, FLAG2));
// Test setting and reading single flags, non-exclusively
flag.Set(FLAG3);
ASSERT_TRUE(flag.Has(FLAG3));
ASSERT_FALSE(flag.Has(FLAG1));
// Test setting and reading multiple flags, non-exclusively
flag.Set(FLAG2, FLAG4);
ASSERT_TRUE(flag.Has(FLAG2, FLAG4));
ASSERT_TRUE(flag.Has(FLAG3));
ASSERT_FALSE(flag.Has(FLAG3, FLAG1));
ASSERT_TRUE(flag.Has(FLAG3, FLAG2, FLAG4));
ASSERT_FALSE(flag.Has(FLAG1));
ASSERT_FALSE(flag.Has(FLAG1, FLAG3, FLAG2, FLAG4));
// Test unsetting and reading multiple flags, non-exclusively
flag.Unset(FLAG3, FLAG1);
ASSERT_FALSE(flag.Has(FLAG3, FLAG1));
ASSERT_FALSE(flag.Has(FLAG3));
ASSERT_TRUE(flag.Has(FLAG2, FLAG4));
ASSERT_FALSE(flag.Has(FLAG3, FLAG2, FLAG4));
ASSERT_FALSE(flag.Has(FLAG1));
ASSERT_FALSE(flag.Has(FLAG1, FLAG3, FLAG2, FLAG4));
}

View File

@ -31,6 +31,60 @@ protected:
} }
}; };
/**
* Test bitset pet flags
*/
TEST_F(PetTest, PetComponentFlagTest) {
using enum PetFlag;
// Test setting and reading single flags, exclusively
petComponent->m_Flags.Reset(NONE);
ASSERT_TRUE(petComponent->m_Flags.HasOnly(NONE));
petComponent->m_Flags.Reset(TAMEABLE);
ASSERT_TRUE(petComponent->m_Flags.HasOnly(TAMEABLE));
ASSERT_FALSE(petComponent->m_Flags.HasOnly(SPAWNING));
// Test setting and reading multiple flags, exclusively
petComponent->m_Flags.Reset(NOT_WAITING, SPAWNING);
ASSERT_FALSE(petComponent->m_Flags.Has(TAMEABLE));
ASSERT_TRUE(petComponent->m_Flags.Has(NOT_WAITING));
ASSERT_TRUE(petComponent->m_Flags.Has(SPAWNING));
ASSERT_TRUE(petComponent->m_Flags.Has(NOT_WAITING, SPAWNING));
ASSERT_FALSE(petComponent->m_Flags.HasOnly(NOT_WAITING));
ASSERT_FALSE(petComponent->m_Flags.HasOnly(SPAWNING));
ASSERT_TRUE(petComponent->m_Flags.HasOnly(NOT_WAITING, SPAWNING));
// Test flags are being properly reset for next batch of tests
petComponent->m_Flags.Reset(NONE);
ASSERT_TRUE(petComponent->m_Flags.Has(NONE));
ASSERT_TRUE(petComponent->m_Flags.HasOnly(NONE));
ASSERT_FALSE(petComponent->m_Flags.Has(NOT_WAITING));
ASSERT_FALSE(petComponent->m_Flags.Has(NOT_WAITING, SPAWNING, TAMEABLE));
// Test setting and reading single flags, non-exclusively
petComponent->m_Flags.Set(NOT_WAITING);
ASSERT_TRUE(petComponent->m_Flags.Has(NOT_WAITING));
ASSERT_FALSE(petComponent->m_Flags.Has(SPAWNING));
// Test setting and reading multiple flags, non-exclusively
petComponent->m_Flags.Set(TAMEABLE, BEING_TAMED);
ASSERT_TRUE(petComponent->m_Flags.Has(TAMEABLE, BEING_TAMED));
ASSERT_TRUE(petComponent->m_Flags.Has(NOT_WAITING));
ASSERT_FALSE(petComponent->m_Flags.Has(NOT_WAITING, SPAWNING));
ASSERT_TRUE(petComponent->m_Flags.Has(NOT_WAITING, TAMEABLE, BEING_TAMED));
ASSERT_FALSE(petComponent->m_Flags.Has(SPAWNING));
ASSERT_FALSE(petComponent->m_Flags.Has(SPAWNING, NOT_WAITING, TAMEABLE, BEING_TAMED));
// Test unsetting and reading multiple flags, non-exclusively
petComponent->m_Flags.Unset(NOT_WAITING, SPAWNING);
ASSERT_FALSE(petComponent->m_Flags.Has(NOT_WAITING, SPAWNING));
ASSERT_FALSE(petComponent->m_Flags.Has(NOT_WAITING));
ASSERT_TRUE(petComponent->m_Flags.Has(TAMEABLE, BEING_TAMED));
ASSERT_FALSE(petComponent->m_Flags.Has(NOT_WAITING, TAMEABLE, BEING_TAMED));
ASSERT_FALSE(petComponent->m_Flags.Has(SPAWNING));
ASSERT_FALSE(petComponent->m_Flags.Has(SPAWNING, NOT_WAITING, TAMEABLE, BEING_TAMED));
}
TEST_F(PetTest, PlacementNewAddComponentTest) { TEST_F(PetTest, PlacementNewAddComponentTest) {
using enum PetFlag; using enum PetFlag;
@ -41,64 +95,9 @@ TEST_F(PetTest, PlacementNewAddComponentTest) {
// Test getting initial status // Test getting initial status
ASSERT_EQ(petComponent->GetParent()->GetObjectID(), 15); ASSERT_EQ(petComponent->GetParent()->GetObjectID(), 15);
ASSERT_TRUE(petComponent->HasFlag(NONE));
ASSERT_EQ(petComponent->GetPetAiState(), PetAiState::spawn); ASSERT_EQ(petComponent->GetPetAiState(), PetAiState::spawn);
ASSERT_EQ(petComponent->GetAbility(), ePetAbilityType::Invalid);
} }
/**
* Test bitset pet flags
*/
TEST_F(PetTest, PetComponentFlagTest) {
using enum PetFlag;
// Test setting and reading single flags, exclusively
petComponent->SetOnlyFlag(NONE);
ASSERT_TRUE(petComponent->HasOnlyFlag(NONE));
petComponent->SetOnlyFlag(TAMEABLE);
ASSERT_TRUE(petComponent->HasOnlyFlag(TAMEABLE));
ASSERT_FALSE(petComponent->HasOnlyFlag(SPAWNING));
// Test setting and reading multiple flags, exclusively
petComponent->SetOnlyFlag(NOT_WAITING, SPAWNING);
ASSERT_FALSE(petComponent->HasFlag(TAMEABLE));
ASSERT_TRUE(petComponent->HasFlag(NOT_WAITING));
ASSERT_TRUE(petComponent->HasFlag(SPAWNING));
ASSERT_TRUE(petComponent->HasFlag(NOT_WAITING, SPAWNING));
ASSERT_FALSE(petComponent->HasOnlyFlag(NOT_WAITING));
ASSERT_FALSE(petComponent->HasOnlyFlag(SPAWNING));
ASSERT_TRUE(petComponent->HasOnlyFlag(NOT_WAITING, SPAWNING));
// Test flags are being properly reset for next batch of tests
petComponent->SetOnlyFlag(NONE);
ASSERT_TRUE(petComponent->HasFlag(NONE));
ASSERT_TRUE(petComponent->HasOnlyFlag(NONE));
ASSERT_FALSE(petComponent->HasFlag(NOT_WAITING));
ASSERT_FALSE(petComponent->HasFlag(NOT_WAITING, SPAWNING, TAMEABLE));
// Test setting and reading single flags, non-exclusively
petComponent->SetFlag(NOT_WAITING);
ASSERT_TRUE(petComponent->HasFlag(NOT_WAITING));
ASSERT_FALSE(petComponent->HasFlag(SPAWNING));
// Test setting and reading multiple flags, non-exclusively
petComponent->SetFlag(TAMEABLE, BEING_TAMED);
ASSERT_TRUE(petComponent->HasFlag(TAMEABLE, BEING_TAMED));
ASSERT_TRUE(petComponent->HasFlag(NOT_WAITING));
ASSERT_FALSE(petComponent->HasFlag(NOT_WAITING, SPAWNING));
ASSERT_TRUE(petComponent->HasFlag(NOT_WAITING, TAMEABLE, BEING_TAMED));
ASSERT_FALSE(petComponent->HasFlag(SPAWNING));
ASSERT_FALSE(petComponent->HasFlag(SPAWNING, NOT_WAITING, TAMEABLE, BEING_TAMED));
// Test unsetting and reading multiple flags, non-exclusively
petComponent->UnsetFlag(NOT_WAITING, SPAWNING);
ASSERT_FALSE(petComponent->HasFlag(NOT_WAITING, SPAWNING));
ASSERT_FALSE(petComponent->HasFlag(NOT_WAITING));
ASSERT_TRUE(petComponent->HasFlag(TAMEABLE, BEING_TAMED));
ASSERT_FALSE(petComponent->HasFlag(NOT_WAITING, TAMEABLE, BEING_TAMED));
ASSERT_FALSE(petComponent->HasFlag(SPAWNING));
ASSERT_FALSE(petComponent->HasFlag(SPAWNING, NOT_WAITING, TAMEABLE, BEING_TAMED));
}
TEST_F(PetTest, PetAiState) { TEST_F(PetTest, PetAiState) {
const auto initialState = petComponent->GetPetAiState(); const auto initialState = petComponent->GetPetAiState();
@ -126,9 +125,6 @@ TEST_F(PetTest, PetUse) {
// Test bouncer logic // Test bouncer logic
ASSERT_FALSE(petComponent->IsHandlingInteraction()); ASSERT_FALSE(petComponent->IsHandlingInteraction());
petComponent->SetAbility(ePetAbilityType::JumpOnObject);
ASSERT_EQ(petComponent->GetAbility(), ePetAbilityType::JumpOnObject);
petComponent->SetInteractType(PetInteractType::bouncer);
petComponent->OnUse(baseEntity); petComponent->OnUse(baseEntity);
// need to add a destroyable component to the test entity and test the imagination drain // need to add a destroyable component to the test entity and test the imagination drain