From 59e5a7e2cec0a43c9ce2ba2a4c16f3d0cd99b801 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Fri, 20 Dec 2024 15:39:39 +0000 Subject: [PATCH] Core: Add entity_cast --- src/map/entities/baseentity.h | 87 ++++++++++++++++++++++++++++++++++- src/map/zone_entities.cpp | 10 ++-- src/map/zone_instance.cpp | 8 ++-- 3 files changed, 95 insertions(+), 10 deletions(-) diff --git a/src/map/entities/baseentity.h b/src/map/entities/baseentity.h index 1947dd27cf2..c4f79ed346e 100644 --- a/src/map/entities/baseentity.h +++ b/src/map/entities/baseentity.h @@ -226,6 +226,15 @@ struct EntityID_t // TODO: Store an incremental u64 as a UUID for the entity for disambiguation in the case of dynamic entities that might have the same targid. }; +class CBaseEntity; +class CCharEntity; +class CNpcEntity; +class CMobEntity; +class CPetEntity; +class CShipEntity; +class CTrustEntity; +class CFellowEntity; + class CAIContainer; class CBattlefield; class CInstance; @@ -250,6 +259,82 @@ struct location_t } }; +// Cast the target entity pointer to the requested entity pointer type. +// We use this because dynamic_cast will succeed for any derived class, not just the requested class. +// ie. A Trust will dynamic_cast to all of: CBattleEntity, CMobEntity, and CTrustEntity, but objtype will only be TYPE_TRUST. +template +constexpr auto entity_cast(TBase basePtr) -> TRequested +{ + static_assert(std::is_pointer_v, "TRequested must be a pointer type."); + static_assert(std::is_base_of_v>, "TRequested must be derived from CBaseEntity."); + + if (basePtr == nullptr || basePtr->objtype == TYPE_NONE) + { + ShowWarning("entity_cast: Invalid entity pointer."); + return nullptr; + } + + switch (basePtr->objtype) + { + case TYPE_PC: + if constexpr (std::is_same_v, CCharEntity>) + { + return static_cast(basePtr); + } + break; + case TYPE_NPC: + if constexpr (std::is_same_v, CNpcEntity>) + { + return static_cast(basePtr); + } + break; + case TYPE_MOB: + if constexpr (std::is_same_v, CMobEntity>) + { + return static_cast(basePtr); + } + break; + case TYPE_PET: + if constexpr (std::is_same_v, CPetEntity>) + { + return static_cast(basePtr); + } + break; + case TYPE_SHIP: + if constexpr (std::is_same_v, CShipEntity>) + { + return static_cast(basePtr); + } + break; + case TYPE_TRUST: + if constexpr (std::is_same_v, CTrustEntity>) + { + return static_cast(basePtr); + } + break; + case TYPE_FELLOW: + if constexpr (std::is_same_v, CFellowEntity>) + { + return static_cast(basePtr); + } + break; + default: + // Fall through to the warning. + break; + } + + // TODO: We're now in an error state. We could use this opportunity to exhaustively dynamic_cast to all possible derived classes + // and print a warning with the results. + + const auto baseName = typeid(TBase).name(); + const auto requestedName = typeid(TRequested).name(); + const auto errString = fmt::format("entity_cast: Failed to cast entity from base type '{}' to requested type '{}'.", baseName, requestedName); + + ShowWarning(errString); + + return nullptr; +} + /************************************************************************ * * * Basic class for all entities in the game * @@ -299,7 +384,7 @@ class CBaseEntity void SetModelId(uint16 modelId); // Set new modelid uint16 GetModelId() const; // Get the modelid - virtual void HandleErrorMessage(std::unique_ptr&){}; + virtual void HandleErrorMessage(std::unique_ptr&) {}; bool IsDynamicEntity() const; diff --git a/src/map/zone_entities.cpp b/src/map/zone_entities.cpp index 6dd5ac5bf99..cb5e276b419 100644 --- a/src/map/zone_entities.cpp +++ b/src/map/zone_entities.cpp @@ -727,10 +727,10 @@ void CZoneEntities::SpawnTRUSTs(CCharEntity* PChar) TracyZoneScoped; for (EntityList_t::const_iterator TrustItr = m_trustList.begin(); TrustItr != m_trustList.end(); ++TrustItr) { - if (CTrustEntity* PCurrentTrust = dynamic_cast(TrustItr->second)) + if (CTrustEntity* PCurrentTrust = entity_cast(TrustItr->second)) { SpawnIDList_t::iterator SpawnTrustItr = PChar->SpawnTRUSTList.find(PCurrentTrust->id); - CCharEntity* PMaster = dynamic_cast(PCurrentTrust->PMaster); + CCharEntity* PMaster = entity_cast(PCurrentTrust->PMaster); // Is this trust "visible" to the player? if (PCurrentTrust->status == STATUS_TYPE::NORMAL && distance(PChar->loc.p, PCurrentTrust->loc.p) <= 50) @@ -1471,7 +1471,7 @@ void CZoneEntities::ZoneServer(time_point tick) EntityList_t::iterator it = m_mobList.begin(); while (it != m_mobList.end()) { - CMobEntity* PMob = dynamic_cast(it->second); + CMobEntity* PMob = entity_cast(it->second); if (!PMob) { ++it; @@ -1560,7 +1560,7 @@ void CZoneEntities::ZoneServer(time_point tick) { for (auto PMobCurrentIter : m_mobList) { - CMobEntity* PCurrentMob = dynamic_cast(PMobCurrentIter.second); + CMobEntity* PCurrentMob = entity_cast(PMobCurrentIter.second); if (PCurrentMob != nullptr && PCurrentMob->isAlive() && PMob->allegiance != PCurrentMob->allegiance && distance(PMob->loc.p, PCurrentMob->loc.p) <= 50) { CMobController* PController = static_cast(PCurrentMob->PAI->GetController()); @@ -1651,7 +1651,7 @@ void CZoneEntities::ZoneServer(time_point tick) it = m_trustList.begin(); while (it != m_trustList.end()) { - if (CTrustEntity* PTrust = dynamic_cast(it->second)) + if (CTrustEntity* PTrust = entity_cast(it->second)) { ShowTrace(fmt::format("CZoneEntities::ZoneServer: Trust: {} ({})", PTrust->getName(), PTrust->id).c_str()); diff --git a/src/map/zone_instance.cpp b/src/map/zone_instance.cpp index 3f300bb2365..793361b408f 100644 --- a/src/map/zone_instance.cpp +++ b/src/map/zone_instance.cpp @@ -451,7 +451,7 @@ void CZoneInstance::CheckTriggerAreas() { for (const auto& [targid, PEntity] : PInstance->m_charList) { - auto* PChar = dynamic_cast(PEntity); + auto* PChar = entity_cast(PEntity); if (!PChar) { continue; @@ -494,7 +494,7 @@ void CZoneInstance::ForEachChar(const std::function& func) { for (const auto& [targid, PEntity] : PInstance->GetCharList()) { - if (auto* PChar = dynamic_cast(PEntity)) + if (auto* PChar = entity_cast(PEntity)) { func(PChar); } @@ -507,7 +507,7 @@ void CZoneInstance::ForEachCharInstance(CBaseEntity* PEntity, const std::functio TracyZoneScoped; for (const auto& [_, PEntity] : PEntity->PInstance->GetCharList()) { - if (auto* PChar = dynamic_cast(PEntity)) + if (auto* PChar = entity_cast(PEntity)) { func(PChar); } @@ -519,7 +519,7 @@ void CZoneInstance::ForEachMobInstance(CBaseEntity* PEntity, const std::function TracyZoneScoped; for (const auto& [_, PEntity] : PEntity->PInstance->m_mobList) { - if (auto* PMob = dynamic_cast(PEntity)) + if (auto* PMob = entity_cast(PEntity)) { func(PMob); }