#include "ProximityMonitorComponent.h" #include "CDClientManager.h" #include "CDClientDatabase.h" #include "CDProximityMonitorComponentTable.h" #include "GameMessages.h" #include "dZoneManager.h" #include "ControllablePhysicsComponent.h" #include "EntityManager.h" #include "SimplePhysicsComponent.h" #include "Amf3.h" #include "dpShapeSphere.h" const std::unordered_set ProximityMonitorComponent::m_EmptyObjectSet = {}; ProximityMonitorComponent::ProximityMonitorComponent(Entity* parent, const int32_t componentID, int radiusSmall, int radiusLarge) : Component(parent, componentID) { if (radiusSmall != -1 && radiusLarge != -1) { SetProximityRadius(radiusSmall, "rocketSmall"); SetProximityRadius(radiusLarge, "rocketLarge"); } RegisterMsg(&ProximityMonitorComponent::OnGetObjectReportInfo); } // The ProximityMonitorComponent row lists ProximityTypes ids ("7,8": RocketSign and WorldLauncher), not radii. Each // type has a name, a radius and where it is loaded: LWOProximityMonitorComponent::LoadDataFromTemplate (0x00c2dfb0) // keeps the ids and SendMessage (0x00cbb930), on AddedToWorld, sets a proximity up under the type's name for each one // that has LoadOnClient. The server's are the ones with LoadOnServer, of a row that has it too. void ProximityMonitorComponent::LoadDataFromTemplate() { if (m_ComponentID <= 0) return; const auto componentID = m_ComponentID; const auto proxCompData = CDClientManager::GetTable()->Query([componentID](const CDProximityMonitorComponent& entry) { return (entry.id == componentID); }); if (proxCompData.empty() || !proxCompData[0].LoadOnServer) return; for (const auto& typeString : GeneralUtils::SplitString(proxCompData[0].Proximities, ',')) { const auto typeID = GeneralUtils::TryParse(typeString); if (!typeID) continue; auto query = CDClientDatabase::CreatePreppedStmt("SELECT Name, Radius, LoadOnServer FROM ProximityTypes WHERE id = ?;"); query.bind(1, *typeID); auto result = query.execQuery(); if (result.eof() || result.getIntField("LoadOnServer", 0) == 0) continue; const std::string name = result.getStringField("Name", ""); if (name.empty()) continue; // A missing or non-positive radius is 30, as the client takes it const auto radius = result.getIntField("Radius", 30); SetProximityRadius(static_cast(radius > 0 ? radius : 30), name); } } ProximityMonitorComponent::~ProximityMonitorComponent() { for (const auto& en : m_ProximitiesData) { if (!en.second) continue; dpWorld::RemoveEntity(en.second); } m_ProximitiesData.clear(); } void ProximityMonitorComponent::SetProximityRadius(float proxRadius, const std::string& name, const uint32_t collisionGroup) { // A proximity set again under its name replaces the old one UnsetProximityRadius(name); dpEntity* en = new dpEntity(m_Parent->GetObjectID(), proxRadius); en->SetCollisionGroup(collisionGroup); en->SetPosition(m_Parent->GetPosition()); dpWorld::AddEntity(en); m_ProximitiesData.insert(std::make_pair(name, en)); } void ProximityMonitorComponent::SetProximityRadius(dpEntity* entity, const std::string& name) { UnsetProximityRadius(name); dpWorld::AddEntity(entity); entity->SetPosition(m_Parent->GetPosition()); m_ProximitiesData.insert(std::make_pair(name, entity)); } void ProximityMonitorComponent::UnsetProximityRadius(const std::string& name) { const auto iter = m_ProximitiesData.find(name); if (iter == m_ProximitiesData.end()) return; if (iter->second) dpWorld::RemoveEntity(iter->second); m_ProximitiesData.erase(iter); } const std::unordered_set& ProximityMonitorComponent::GetProximityObjects(const std::string& name) const { const auto iter = m_ProximitiesData.find(name); if (iter == m_ProximitiesData.cend()) { return m_EmptyObjectSet; } return iter->second->GetCurrentlyCollidingObjects(); } bool ProximityMonitorComponent::IsInProximity(const std::string& name, LWOOBJID objectID) { const auto iter = m_ProximitiesData.find(name); if (iter == m_ProximitiesData.cend()) { return false; } const auto& collisions = iter->second->GetCurrentlyCollidingObjects(); return collisions.contains(objectID); } bool ProximityMonitorComponent::OnGetObjectReportInfo(GameMessages::GetObjectReportInfo& reportInfo) { auto& proxInfo = reportInfo.info->PushDebug("Proximity Monitor"); for (const auto& [name, entity] : m_ProximitiesData) { if (!entity) continue; auto& proxAmf = proxInfo.PushDebug(name); const auto* const shape = entity->GetShape(); if (shape && shape->GetShapeType() == dpShapeType::Sphere) { const auto* const sphere = static_cast(shape); proxAmf.PushDebug("Radius") = sphere->GetRadius(); } proxAmf.PushDebug("Sleeping") = entity->GetSleeping(); proxAmf.PushDebug("Scale") = entity->GetScale(); proxAmf.PushDebug("Gargantuan") = entity->GetIsGargantuan(); proxAmf.PushDebug("Static") = entity->GetIsStatic(); proxAmf.PushDebug("Position").PushDebug(entity->GetPosition()); proxAmf.PushDebug("Rotation").PushDebug(entity->GetRotation()); auto& collidingAmf = proxAmf.PushDebug("Colliding Objects"); int i = 1; for (const auto& colliding : entity->GetCurrentlyCollidingObjects()) { collidingAmf.PushDebug(std::to_string(i++), "LWOOBJID") = std::to_string(colliding); } } return true; } void ProximityMonitorComponent::Run(float deltaTime) { for (const auto& prox : m_ProximitiesData) { if (!prox.second) continue; prox.second->SetPosition(m_Parent->GetPosition()); //Process enter events for (const auto id : prox.second->GetNewObjects()) { m_Parent->OnCollisionProximity(id, prox.first, "ENTER"); } //Process exit events for (const auto id : prox.second->GetRemovedObjects()) { m_Parent->OnCollisionProximity(id, prox.first, "LEAVE"); } } }