Use compile-time flag setting

This commit is contained in:
jadebenn 2024-04-20 14:17:34 -05:00
parent 5ddf4ce28b
commit f41321eb7b
7 changed files with 196 additions and 64 deletions

View File

@ -1,81 +1,158 @@
#include "GeneralUtils.h" #include "GeneralUtils.h"
#include <bit> /**
// TODO: Test bounds checking * A bitset flag class capable of compile-time bounds-checking.
* It should be possible to unify its methods with C++23, but until then
* use the compile-time overloads preferentially.
*/
template <typename T> template <typename T>
requires std::is_enum_v<T> requires std::is_enum_v<T>
class Flag { class Flag {
public: public:
// Type aliases
using type = T; using type = T;
using underlying_type = std::underlying_type_t<T>; using underlying_type = std::underlying_type_t<T>;
static constexpr auto MAX_FLAG_VAL = std::bit_width(sizeof(underlying_type));
// Static constants
static constexpr auto MAX_FLAG_VAL = sizeof(underlying_type) * CHAR_BIT;
// Constructors
Flag() = default;
// Run-time constructor
constexpr Flag(const T value) : m_Flags{ ConvertFlag(value) } {}
/** /**
* Sets one or more flags * RUNTIME: Sets one or more flags
* @param flag Flag(s) to set * @param flag Flag(s) to set
*/ */
template <std::same_as<T>... varArg> template <std::same_as<T>... varArg>
constexpr void Set(varArg... flag) noexcept { constexpr void Set(const varArg... flag) noexcept {
m_Flags |= (ConvertFlag(flag) | ...); m_Flags |= (ConvertFlag(flag) | ...);
} }
/** /**
* Sets ONLY have the specified flag(s), clearing all others * COMPILETIME: Sets one or more flags
* @param flag Flag(s) to set
*/
template <T... flag>
constexpr void Set() noexcept {
m_Flags |= (ConvertFlag<flag>() | ...);
}
/**
* RUNTIME: Sets ONLY have the specified flag(s), clearing all others
* @param flag Flag(s) to set exclusively * @param flag Flag(s) to set exclusively
*/ */
template <std::same_as<T>... varArg> template <std::same_as<T>... varArg>
constexpr void Reset(varArg... flag) { constexpr void Reset(const varArg... flag) {
m_Flags = (ConvertFlag(flag) | ...); m_Flags = (ConvertFlag(flag) | ...);
} }
/** /**
* Unsets one or more flags * COMPILETIME: Sets ONLY have the specified flag(s), clearing all others
* @param flag Flag(s) to set exclusively
*/
template <T... flag>
constexpr void Reset() noexcept {
m_Flags = (ConvertFlag<flag>() | ...);
}
/**
* RUNTIME: Unsets one or more flags
* @param flag Flag(s) to unset * @param flag Flag(s) to unset
*/ */
template <std::same_as<T>... varArg> template <std::same_as<T>... varArg>
constexpr void Unset(varArg... flag) { constexpr void Unset(const varArg... flag) {
m_Flags &= ~(ConvertFlag(flag) | ...); m_Flags &= ~(ConvertFlag(flag) | ...);
} }
/** /**
* Returns true all the specified flag(s) are set * COMPILETIME: Unsets one or more flags
* @param flag Flag(s) to unset
*/
template <T... flag>
constexpr void Unset() noexcept {
m_Flags &= ~(ConvertFlag<flag>() | ...);
}
/**
* RUNTIME: Returns true all the specified flag(s) are set
* @param flag Flag(s) to check * @param flag Flag(s) to check
*/ */
template <std::same_as<T>... varArg> template <std::same_as<T>... varArg>
constexpr bool Has(varArg... flag) const { [[nodiscard]]
constexpr bool Has(const varArg... flag) const {
return (m_Flags & (ConvertFlag(flag) | ...)) == (ConvertFlag(flag) | ...); return (m_Flags & (ConvertFlag(flag) | ...)) == (ConvertFlag(flag) | ...);
} }
/** /**
* Returns true ONLY the specified flag(s) are set * COMPILETIME: Returns true if all the specified flag(s) are set
* @param flag Flag(s) to check
*/
template <T... flag>
[[nodiscard]]
constexpr bool Has() const noexcept {
return (m_Flags & (ConvertFlag<flag>() | ...)) == (ConvertFlag<flag>() | ...);
}
/**
* RUNTIME: Returns true if ONLY the specified flag(s) are set
* @param flag Flag(s) to check * @param flag Flag(s) to check
*/ */
template <std::same_as<T>... varArg> template <std::same_as<T>... varArg>
constexpr bool HasOnly(varArg... flag) const { [[nodiscard]]
constexpr bool HasOnly(const varArg... flag) const {
return m_Flags == (ConvertFlag(flag) | ...); return m_Flags == (ConvertFlag(flag) | ...);
} }
/**
* COMPILETIME: Returns true if ONLY the specified flag(s) are set
* @param flag Flag(s) to check
*/
template <T... flag>
[[nodiscard]]
constexpr bool HasOnly() const noexcept {
return m_Flags == (ConvertFlag<flag>() | ...);
}
/**
* @return the raw value of the flags set
*/
[[nodiscard]]
constexpr T Value() const noexcept {
return static_cast<T>(m_Flags);
}
/** /**
* Operator overload to allow for '=' assignment * Operator overload to allow for '=' assignment
*/ */
constexpr Flag& operator=(const T value) { constexpr Flag& operator=(const T value) {
Reset(value); m_Flags = ConvertFlag(value);
return *this; return *this;
} }
private: private:
template <T flag>
[[nodiscard]]
static consteval underlying_type ConvertFlag() noexcept {
constexpr auto flag_val = GeneralUtils::ToUnderlying(flag);
static_assert(flag_val <= MAX_FLAG_VAL, "Flag value is too large to set!");
return flag_val != 0 ? 1 << flag_val : flag_val;
}
[[nodiscard]] [[nodiscard]]
static constexpr underlying_type ConvertFlag(const T flag) { static constexpr underlying_type ConvertFlag(const T flag) {
auto flag_val = GeneralUtils::ToUnderlying(flag); auto flag_val = GeneralUtils::ToUnderlying(flag);
if (flag_val != 0) {
return 1 << flag_val; // This is less-efficient than the compile-time check, but still works
// We can probably unify this and the above functions with C++23 and 'if consteval'
if (flag_val > MAX_FLAG_VAL) {
throw std::runtime_error{ "Flag value is too large to set!" };
} }
// This should theoeretically be possible to do at compile time, but idk how
if (std::bit_width(flag_val) > MAX_FLAG_VAL) { return flag_val != 0 ? 1 << flag_val : flag_val;
throw std::runtime_error{ "Enum value too large to be a flag!" };
}
return flag_val;
} }
underlying_type m_Flags; underlying_type m_Flags;

View File

@ -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{ const 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
@ -75,10 +75,12 @@ std::map<LOT, int32_t> PetComponent::petFlags{
{ 13067, 838 }, // Skeleton dragon { 13067, 838 }, // Skeleton dragon
}; };
PetComponent::PetComponent(Entity* parentEntity, uint32_t componentId) : Component{ parentEntity } { PetComponent::PetComponent(Entity* parentEntity, uint32_t componentId)
: Component{ parentEntity }
, m_Flags{ PetFlag::SPAWNING }
{
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_Flags = PetFlag::SPAWNING; // Tameable
m_StartPosition = m_Parent->GetPosition(); m_StartPosition = m_Parent->GetPosition();
m_MovementAI = nullptr; m_MovementAI = nullptr;
@ -99,7 +101,7 @@ void PetComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpd
constexpr bool isDirty = true; constexpr bool isDirty = true;
outBitStream.Write(isDirty); // Always serialize as dirty for now outBitStream.Write(isDirty); // Always serialize as dirty for now
outBitStream.Write(m_Flags); outBitStream.Write(m_Flags.Value());
outBitStream.Write(tamed ? m_Interaction.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.obj != LWOOBJID_EMPTY; const bool interacting = m_Interaction.obj != LWOOBJID_EMPTY;
@ -205,12 +207,13 @@ void PetComponent::OnUse(Entity* originator) {
buildFile = std::string(result.getStringField("ValidPiecesLXF")); buildFile = std::string(result.getStringField("ValidPiecesLXF"));
PuzzleData data; PuzzleData data{
data.buildFile = buildFile; .puzzleModelLot = result.getIntField("PuzzleModelLot"),
data.puzzleModelLot = result.getIntField("PuzzleModelLot"); .buildFile = buildFile,
data.timeLimit = result.getFloatField("Timelimit"); .timeLimit = static_cast<float>(result.getFloatField("Timelimit")),
data.numValidPieces = result.getIntField("NumValidPieces"); .imaginationCost = result.getIntField("imagCostPerBuild"),
data.imaginationCost = result.getIntField("imagCostPerBuild"); .numValidPieces = result.getIntField("NumValidPieces")
};
if (data.timeLimit <= 0) data.timeLimit = 60; if (data.timeLimit <= 0) data.timeLimit = 60;
imaginationCost = data.imaginationCost; imaginationCost = data.imaginationCost;
@ -306,7 +309,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();
m_Flags.Set(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());
@ -334,7 +337,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 (m_Flags.Has(PetFlag::SPAWNING)) OnSpawn(); if (m_Flags.Has<PetFlag::SPAWNING>()) OnSpawn();
// Handle pet AI states // Handle pet AI states
switch (m_State) { switch (m_State) {
@ -501,7 +504,7 @@ void PetComponent::NotifyTamingBuildSuccess(const NiPoint3 position) {
missionComponent->Progress(eMissionTaskType::PET_TAMING, m_Parent->GetLOT()); missionComponent->Progress(eMissionTaskType::PET_TAMING, m_Parent->GetLOT());
} }
m_Flags.Reset(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) {
@ -612,7 +615,7 @@ void PetComponent::ClientExitTamingMinigame(bool voluntaryExit) {
currentActivities.erase(m_Tamer); currentActivities.erase(m_Tamer);
m_Flags.Reset(PetFlag::TAMEABLE); m_Flags.Reset<PetFlag::TAMEABLE>();
m_Tamer = LWOOBJID_EMPTY; m_Tamer = LWOOBJID_EMPTY;
m_Timer = 0; m_Timer = 0;
@ -661,7 +664,7 @@ void PetComponent::ClientFailTamingMinigame() {
currentActivities.erase(m_Tamer); currentActivities.erase(m_Tamer);
m_Flags.Reset(PetFlag::TAMEABLE); m_Flags.Reset<PetFlag::TAMEABLE>();
m_Tamer = LWOOBJID_EMPTY; m_Tamer = LWOOBJID_EMPTY;
m_Timer = 0; m_Timer = 0;
@ -721,15 +724,14 @@ void PetComponent::OnSpawn() {
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);
SetPetAiState(PetAiState::follow); SetPetAiState(PetAiState::follow);
} else { } else {
m_Flags.Set(PetFlag::TAMEABLE); m_Flags.Set<PetFlag::TAMEABLE>();
SetPetAiState(PetAiState::idle); SetPetAiState(PetAiState::idle);
} }
m_Flags.Set(PetFlag::IDLE); m_Flags.Set<PetFlag::IDLE>();
m_Flags.Unset(PetFlag::SPAWNING); m_Flags.Unset<PetFlag::SPAWNING>();
Game::entityManager->SerializeEntity(m_Parent); Game::entityManager->SerializeEntity(m_Parent);
} }
@ -848,7 +850,7 @@ void PetComponent::StopInteract(bool bDontSerialize) {
m_Interaction.type = PetInteractType::none; m_Interaction.type = PetInteractType::none;
m_Interaction.ability = petAbility; m_Interaction.ability = petAbility;
SetPetAiState(PetAiState::follow); SetPetAiState(PetAiState::follow);
m_Flags.Reset(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);
@ -871,8 +873,8 @@ void PetComponent::SetupInteractBouncer() {
constexpr auto petAbility = ePetAbilityType::JumpOnObject; constexpr auto petAbility = ePetAbilityType::JumpOnObject;
m_Interaction.ability = petAbility; m_Interaction.ability = petAbility;
m_Flags.Unset(PetFlag::IDLE); m_Flags.Unset<PetFlag::IDLE>();
m_Flags.Set(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>(); // 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
@ -913,8 +915,6 @@ void PetComponent::StartInteractBouncer() {
SetIsHandlingInteraction(true); SetIsHandlingInteraction(true);
SwitchComponent* closestSwitch = SwitchComponent::GetClosestSwitch(m_MovementAI->GetDestination()); // TODO: Find a better way to do this SwitchComponent* closestSwitch = SwitchComponent::GetClosestSwitch(m_MovementAI->GetDestination()); // TODO: Find a better way to do this
closestSwitch->EntityEnter(m_Parent); closestSwitch->EntityEnter(m_Parent);
//m_Timer += 0.5;
} }
void PetComponent::HandleInteractBouncer() { void PetComponent::HandleInteractBouncer() {
@ -954,8 +954,8 @@ void PetComponent::SetupInteractTreasureDig() {
constexpr auto petAbility = ePetAbilityType::DigAtPosition; constexpr auto petAbility = ePetAbilityType::DigAtPosition;
m_Interaction.ability = petAbility; m_Interaction.ability = petAbility;
m_Flags.Unset(PetFlag::IDLE); m_Flags.Unset<PetFlag::IDLE>();
m_Flags.Set(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
@ -989,8 +989,8 @@ void PetComponent::StartInteractTreasureDig() {
Game::entityManager->SerializeEntity(user); Game::entityManager->SerializeEntity(user);
SetIsHandlingInteraction(true); SetIsHandlingInteraction(true);
m_Flags.Unset(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
m_Flags.Set(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);
@ -1039,7 +1039,7 @@ void PetComponent::Activate(Item* item, bool registerPet, bool fromTaming) { //
auto* const owner = GetOwner(); auto* const owner = GetOwner();
if (!owner) return; if (!owner) return;
m_Flags.Set(PetFlag::SPAWNING); m_Flags.Set<PetFlag::SPAWNING>();
auto databaseData = inventoryComponent->GetDatabasePet(m_DatabaseId); auto databaseData = inventoryComponent->GetDatabasePet(m_DatabaseId);

View File

@ -379,7 +379,7 @@ private:
/** /**
* The time limit to complete the build * The time limit to complete the build
*/ */
int32_t timeLimit; float timeLimit;
/** /**
* The imagination cost for the tamer to start the minigame * The imagination cost for the tamer to start the minigame
@ -432,7 +432,7 @@ private:
/** /**
* 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
*/ */
static std::map<LOT, int32_t> petFlags; static const std::map<LOT, int32_t> petFlags;
/** /**
* The halting radius of the pet while following a player TODO: Move into struct? * The halting radius of the pet while following a player TODO: Move into struct?

View File

@ -523,14 +523,14 @@ void GameMessages::SendNotifyClientFlagChange(const LWOOBJID& objectID, uint32_t
SEND_PACKET; SEND_PACKET;
} }
void GameMessages::SendHelp(const LWOOBJID& objectId, const eHelpType help, const SystemAddress& sysAddr) { void GameMessages::SendHelp(const LWOOBJID objectId, const eHelpType help, const SystemAddress& sysAddr) {
CBITSTREAM; CBITSTREAM;
CMSGHEADER; CMSGHEADER;
bitStream.Write(objectId); bitStream.Write(objectId);
bitStream.Write(eGameMessageType::HELP); bitStream.Write(eGameMessageType::HELP);
bitStream.Write(help); bitStream.Write(help);
SEND_PACKET; SEND_PACKET;
} }
@ -1186,7 +1186,7 @@ void GameMessages::SendPlayerReachedRespawnCheckpoint(Entity* entity, const NiPo
const bool bIsNotIdentity = rotation != NiQuaternionConstant::IDENTITY; const bool bIsNotIdentity = rotation != NiQuaternionConstant::IDENTITY;
bitStream.Write(bIsNotIdentity); bitStream.Write(bIsNotIdentity);
if (bIsNotIdentity) { if (bIsNotIdentity) {
bitStream.Write(rotation.w); bitStream.Write(rotation.w);
bitStream.Write(rotation.x); bitStream.Write(rotation.x);

View File

@ -86,7 +86,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 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 SendNotifyClientFlagChange(const LWOOBJID& objectID, uint32_t iFlagID, bool bFlag, const SystemAddress& sysAddr);
void SendHelp(const LWOOBJID& objectId, const eHelpType help, 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 SendChangeObjectWorldState(const LWOOBJID& objectID, eObjectWorldState state, const SystemAddress& sysAddr);
void SendOfferMission(const LWOOBJID& entity, const SystemAddress& sysAddr, int32_t missionID, const LWOOBJID& offererID); void SendOfferMission(const LWOOBJID& entity, const SystemAddress& sysAddr, int32_t missionID, const LWOOBJID& offererID);

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->m_Flags.Has(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->m_Flags.Set(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->m_Flags.Set(PetFlag::TAMEABLE, PetFlag::UNKNOWN2); petComponent->m_Flags.Set<PetFlag::TAMEABLE, PetFlag::UNKNOWN2>();
} }
auto* combatAIComponent = self->GetComponent<BaseCombatAIComponent>(); auto* combatAIComponent = self->GetComponent<BaseCombatAIComponent>();

View File

@ -10,9 +10,65 @@ enum class TestFlag : uint8_t {
}; };
/** /**
* Test bitset flags * Test bitset flags (compile-time methods)
*/ */
TEST(FlagTests, FlagMethodTest) { TEST(FlagTests, FlagMethodTestCompileTime) {
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>()));
}
/**
* Test bitset flags (run-time methods)
*/
TEST(FlagTests, FlagMethodTestRunTime) {
using enum TestFlag; using enum TestFlag;
auto flag = Flag<TestFlag>{}; auto flag = Flag<TestFlag>{};
@ -64,4 +120,3 @@ TEST(FlagTests, FlagMethodTest) {
ASSERT_FALSE(flag.Has(FLAG1)); ASSERT_FALSE(flag.Has(FLAG1));
ASSERT_FALSE(flag.Has(FLAG1, FLAG3, FLAG2, FLAG4)); ASSERT_FALSE(flag.Has(FLAG1, FLAG3, FLAG2, FLAG4));
} }