diff --git a/Mammoth/Include/TSEDeviceClassesImpl.h b/Mammoth/Include/TSEDeviceClassesImpl.h index 575da9eb7..9d582ef05 100644 --- a/Mammoth/Include/TSEDeviceClassesImpl.h +++ b/Mammoth/Include/TSEDeviceClassesImpl.h @@ -27,6 +27,11 @@ class CWeaponTargetDefinition static std::unique_ptr ReadFromStream (SLoadCtx& Ctx); void WriteToStream (IWriteStream* pStream) const; private: + const std::function fnExclude = + [](const CSpaceObject* pSource, const CSpaceObject* pObj, const CVector vCenter, const int iMinAngle, const int iMaxAngle) { + return pObj->IsIntangible(); + }; + Kernel::CString m_CriteriaString = ""; CSpaceObjectCriteria m_TargetCriteria; bool m_bCheckLineOfFire = false; // Check line of fire for friendlies diff --git a/Mammoth/Include/TSESpaceObjectsEnum.h b/Mammoth/Include/TSESpaceObjectsEnum.h index d804c76e3..bc7dc1ec2 100644 --- a/Mammoth/Include/TSESpaceObjectsEnum.h +++ b/Mammoth/Include/TSESpaceObjectsEnum.h @@ -5,6 +5,8 @@ #pragma once +#include + // RANGE FILTERS -------------------------------------------------------------- // CNearestInRadiusRange @@ -182,6 +184,7 @@ class CCriteriaObjSelector bool Matches (CSpaceObject &Obj, Metric rDist2) { return (!Obj.IsUnreal()) + && Obj.MatchesCriteriaCategory(m_Ctx, m_Criteria) && Obj.MatchesCriteria(m_Ctx, m_Criteria); } @@ -683,6 +686,11 @@ class CSpaceObjectEnum template static CSpaceObject *FindNearestTangibleObjInArc (const CSystem &System, const CSpaceObject *pSource, const CVector vCenter, const int iMinAngle, const int iMaxAngle, RANGE &Range, SELECTOR &Selector) { + // TODO(heliogenesis): Create variants of this for other areas where we can optimize via grid. + // For AutoDefenseClass, we can use this as is but we also need to exclude pObj->GetDamageSource().IsAutomatedWeapon() == TRUE, + // and ((!pObj->GetDamageSource().IsEjecta() && !pObj->GetDamageSource().IsExplosion())) if the auto defense class only targets reactions. + // We may want to pass in a function pointer that accepts pObj, pSource, vCenter as arguments so we can avoid introducing an excessive number of functions like this. + // If we go this route, we should remove the `!pObj->IsIntangible()` function, and add that to the function pointer for intangibility. const CSpaceObjectGrid &Grid = System.GetObjectGrid(); Range.ReduceRadius(Selector.GetMaxRange()); @@ -715,6 +723,98 @@ class CSpaceObjectEnum return pBestObj; } + template + static CSpaceObject* FindNearestObjInArc(const CSystem& System, const CSpaceObject* pSource, const CVector vCenter, const int iMinAngle, const int iMaxAngle, RANGE& Range, SELECTOR& Selector) + { + const std::function fnExclude = + [](const CSpaceObject* pSource, const CSpaceObject* pObj, const CVector vCenter, const int iMinAngle, const int iMaxAngle) { + return true; + }; + return FindNearestObjInArc(System, pSource, vCenter, iMinAngle, iMaxAngle, fnExclude, Range, Selector); + } + + template + static CSpaceObject *FindNearestObjInArc(const CSystem &System, const CSpaceObject *pSource, const CVector vCenter, const int iMinAngle, const int iMaxAngle, + const std::function fnExclude, RANGE &Range, SELECTOR &Selector) + { + const CSpaceObjectGrid &Grid = System.GetObjectGrid(); + + Range.ReduceRadius(Selector.GetMaxRange()); + + CSpaceObject *pBestObj = NULL; + + SSpaceObjectGridEnumerator i; + Grid.EnumStart(i, Range.GetUR(), Range.GetLL(), gridNoBoxCheck); + + bool bOmnidirectional = (iMinAngle == -1 && iMaxAngle == -1); + + while (Grid.EnumHasMore(i)) + { + CSpaceObject *pObj = Grid.EnumGetNextFast(i); + + Metric rDist2; + if (Selector.MatchesCategory(*pObj) + && Range.Matches(*pObj, &rDist2) + && Selector.Matches(*pObj, rDist2) + && pObj != pSource + && !fnExclude(pSource, pObj, vCenter, iMinAngle, iMaxAngle) + && (bOmnidirectional + || AngleInArc(VectorToPolar((pObj->GetPos() - vCenter)), iMinAngle, iMaxAngle))) + { + Range.SetBestDist2(rDist2); + pBestObj = pObj; + } + } + + return pBestObj; + } + + template + static TArray FindNearbyObjsInArc(const CSystem& System, const CSpaceObject* pSource, const CVector vCenter, const int iMinAngle, const int iMaxAngle, RANGE& Range, SELECTOR& Selector) + { + const std::function fnExclude = + [](const CSpaceObject* pSource, const CSpaceObject* pObj, const CVector vCenter, const int iMinAngle, const int iMaxAngle) { + return true; + }; + return FindNearbyObjsInArc(System, pSource, vCenter, iMinAngle, iMaxAngle, fnExclude, Range, Selector); + } + + template + static TArray FindNearbyObjsInArc(const CSystem &System, const CSpaceObject *pSource, const CVector vCenter, const int iMinAngle, const int iMaxAngle, + const std::function fnExclude, RANGE &Range, SELECTOR &Selector) + { + const CSpaceObjectGrid &Grid = System.GetObjectGrid(); + + Range.ReduceRadius(Selector.GetMaxRange()); + + CSpaceObject *pBestObj = NULL; + + SSpaceObjectGridEnumerator i; + Grid.EnumStart(i, Range.GetUR(), Range.GetLL(), gridNoBoxCheck); + + bool bOmnidirectional = (iMinAngle == -1 && iMaxAngle == -1); + + TArray Objects; + while (Grid.EnumHasMore(i)) + { + CSpaceObject *pObj = Grid.EnumGetNextFast(i); + + Metric rDist2; + if (Selector.MatchesCategory(*pObj) + && Range.Matches(*pObj, &rDist2) + && Selector.Matches(*pObj, rDist2) + && pObj != pSource + && !fnExclude(pSource, pObj, vCenter, iMinAngle, iMaxAngle) + && (bOmnidirectional + || AngleInArc(VectorToPolar((pObj->GetPos() - vCenter)), iMinAngle, iMaxAngle))) + { + Objects.Insert(pObj); + } + } + + return Objects; + } + template static CSpaceObject *FindObjInRange (const CSystem &System, RANGE &Range, SELECTOR &Selector) { diff --git a/Mammoth/Include/TSESystem.h b/Mammoth/Include/TSESystem.h index 985db7e08..7a78bd13a 100644 --- a/Mammoth/Include/TSESystem.h +++ b/Mammoth/Include/TSESystem.h @@ -488,7 +488,9 @@ class CSystem CSpaceObject *EnumObjectsInBoxPointGetNext (SSpaceObjectGridEnumerator &i) const { return m_ObjGrid.EnumGetNextInBoxPoint(i); } CSpaceObject *FindObject (DWORD dwID) const; CSpaceObject *FindNearestObject (CSpaceObject* pSource, const CVector &vCenter, Metric rRange, const CSpaceObjectCriteria &Criteria = CSpaceObjectCriteria()) const; - CSpaceObject *FindNearestTangibleObjectInArc (CSpaceObject* pSource, const CVector &vCenter, Metric rRange, const CSpaceObjectCriteria &Criteria = CSpaceObjectCriteria(), int iMinAngle = -1, int iMaxAngle = -1) const; + CSpaceObject *FindNearestObjectInArc (CSpaceObject* pSource, const CVector &vCenter, Metric rRange, + const std::function fnExclude, + const CSpaceObjectCriteria &Criteria = CSpaceObjectCriteria(), int iMinAngle = -1, int iMaxAngle = -1) const; CSpaceObject *FindObjectInRange (CSpaceObject *pSource, const CVector &vCenter, Metric rRange, const CSpaceObjectCriteria &Criteria = CSpaceObjectCriteria()) const; CSpaceObject *FindObjectWithOrbit (const COrbit &Orbit) const; bool FindObjectName (const CSpaceObject *pObj, CString *retsName = NULL); diff --git a/Mammoth/TSE/AIManeuvers.cpp b/Mammoth/TSE/AIManeuvers.cpp index 19311db0f..62fd3f058 100644 --- a/Mammoth/TSE/AIManeuvers.cpp +++ b/Mammoth/TSE/AIManeuvers.cpp @@ -149,6 +149,7 @@ bool CAIBehaviorCtx::CalcFlockingFormationCloud (CShip *pShip, CSpaceObject *pLe Metric rFlockCount = 0.0; Metric rAvoidCount = 0.0; + // TODO(heliogenesis): Use grid functions to optimize this code. for (i = 0; i < pShip->GetSystem()->GetObjectCount(); i++) { CSpaceObject *pObj = pShip->GetSystem()->GetObject(i); diff --git a/Mammoth/TSE/CAIBehaviorCtx.cpp b/Mammoth/TSE/CAIBehaviorCtx.cpp index 4ec1e213a..9fbf4c692 100644 --- a/Mammoth/TSE/CAIBehaviorCtx.cpp +++ b/Mammoth/TSE/CAIBehaviorCtx.cpp @@ -643,6 +643,7 @@ bool CAIBehaviorCtx::CalcNavPath (CShip *pShip, CSpaceObject *pTo) CSpaceObject *pBestObj = NULL; Metric rBestDist2 = MAX_NAV_START_DIST2; + // TODO(heliogenesis): Use grid optimizations for this code for (i = 0; i < pSystem->GetObjectCount(); i++) { CSpaceObject *pObj = pSystem->GetObject(i); diff --git a/Mammoth/TSE/CAutoDefenseClass.cpp b/Mammoth/TSE/CAutoDefenseClass.cpp index 0d3092dcc..0507e8c27 100644 --- a/Mammoth/TSE/CAutoDefenseClass.cpp +++ b/Mammoth/TSE/CAutoDefenseClass.cpp @@ -85,51 +85,18 @@ CSpaceObject *CAutoDefenseClass::FindTarget (CInstalledDevice *pDevice, CSpaceOb { Metric rBestDist2 = m_rInterceptRange * m_rInterceptRange; - for (int i = 0; i < pSystem->GetObjectCount(); i++) - { - CSpaceObject *pObj = pSystem->GetObject(i); - - // See if this is a valid target. If not, skip - - if (pObj == NULL - || pObj->GetCategory() != CSpaceObject::catMissile - || pObj->GetDamageSource().IsEqual(pSource) - || pObj->IsIntangible() - || pObj->GetDamageSource().IsAutomatedWeapon() - || !pSource->IsAngryAt(pObj->GetDamageSource()) - || (!isOmniDirectional - && !AngleInArc(VectorToPolar((pObj->GetPos() - vSourcePos)), iMinFireArc, iMaxFireArc))) - continue; - - if (!m_bTargetReactions - && (pObj->GetDamageSource().IsEjecta() || pObj->GetDamageSource().IsExplosion())) - continue; - - // Is this closer than our previous best. If not, skip. - - CVector vRange = pObj->GetPos() - vSourcePos; - Metric rDistance2 = vRange.Dot(vRange); - - if (rDistance2 >= rBestDist2) - continue; - - // If we have restrictions on the source distance, then check - // here. Skip if we don't match. - - if (m_rMinSourceRange2 > 0.0) - { - CSpaceObject *pMissileSource = pObj->GetDamageSource().GetObj(); - if (pMissileSource && pSource->GetDistance2(pMissileSource) < m_rMinSourceRange2) - continue; - } - - // If we get this far, then this is the best target so far. - - pBestTarget = pObj; - rBestDist2 = rDistance2; - } - - return pBestTarget; + const std::function fnExclude = + [this](const CSpaceObject* pSource, const CSpaceObject* pObj, const CVector vCenter, const int iMinAngle, const int iMaxAngle) { + // Exclude objects that are intangible, or ejecta/explosions if we are not targeting reactions + CSpaceObject* pMissileSource = this->m_rMinSourceRange2 > 0.0 ? pObj->GetDamageSource().GetObj() : nullptr; + return pObj->IsIntangible() || (!m_bTargetReactions && (pObj->GetDamageSource().IsEjecta() || pObj->GetDamageSource().IsExplosion())) || + pObj->GetCategory() != CSpaceObject::catMissile || pObj->GetDamageSource().IsEqual(*pSource) + || pObj->GetDamageSource().IsAutomatedWeapon() || !pSource->IsAngryAt(pObj->GetDamageSource()) || + (pMissileSource && pSource->GetDistance2(pMissileSource) < this->m_rMinSourceRange2); + }; + + CSpaceObjectCriteria EmptyCriteria; + return pSystem->FindNearestObjectInArc(pSource, vSourcePos, m_rInterceptRange, fnExclude, EmptyCriteria, iMinFireArc, iMaxFireArc); } // Look for an object by criteria @@ -138,36 +105,23 @@ CSpaceObject *CAutoDefenseClass::FindTarget (CInstalledDevice *pDevice, CSpaceOb { // Compute the range - Metric rBestDist2; + Metric rBestDist; if (m_TargetCriteria.MatchesMaxRadius() < g_InfiniteDistance) - rBestDist2 = (m_TargetCriteria.MatchesMaxRadius() * m_TargetCriteria.MatchesMaxRadius()); + rBestDist = m_TargetCriteria.MatchesMaxRadius(); else - rBestDist2 = m_rInterceptRange * m_rInterceptRange; + rBestDist = m_rInterceptRange; // Now look for the nearest object + const std::function fnExclude = + [this](const CSpaceObject* pSource, const CSpaceObject* pObj, const CVector vCenter, const int iMinAngle, const int iMaxAngle) { + // Exclude objects that are intangible, or ejecta/explosions if we are not targeting reactions + return pObj->IsIntangible() || (!this->m_bTargetReactions && (pObj->GetDamageSource().IsEjecta() || pObj->GetDamageSource().IsExplosion())) + || pObj->GetDamageSource().IsAutomatedWeapon(); + }; + CSpaceObjectCriteria::SCtx Ctx(pSource, m_TargetCriteria); - for (int i = 0; i < pSystem->GetObjectCount(); i++) - { - CSpaceObject *pObj = pSystem->GetObject(i); - Metric rDistance2; - if (pObj - && pObj->MatchesCriteriaCategory(Ctx, m_TargetCriteria) - && ((rDistance2 = (pObj->GetPos() - vSourcePos).Length2()) < rBestDist2) - && pObj->MatchesCriteria(Ctx, m_TargetCriteria) - && !pObj->IsIntangible() - && pObj != pSource - && !pObj->GetDamageSource().IsAutomatedWeapon() - && (m_bTargetReactions || (!pObj->GetDamageSource().IsEjecta() && !pObj->GetDamageSource().IsExplosion())) - && (isOmniDirectional - || AngleInArc(VectorToPolar((pObj->GetPos() - vSourcePos)), iMinFireArc, iMaxFireArc))) - { - pBestTarget = pObj; - rBestDist2 = rDistance2; - } - } - - return pBestTarget; + return pSystem->FindNearestObjectInArc(pSource, vSourcePos, rBestDist, fnExclude, m_TargetCriteria, iMinFireArc, iMaxFireArc); } default: diff --git a/Mammoth/TSE/CSystem.cpp b/Mammoth/TSE/CSystem.cpp index 525bf9824..7bfbe5cec 100644 --- a/Mammoth/TSE/CSystem.cpp +++ b/Mammoth/TSE/CSystem.cpp @@ -1830,7 +1830,9 @@ CSpaceObject *CSystem::FindNearestObject (CSpaceObject *pSource, const CVector & } } -CSpaceObject *CSystem::FindNearestTangibleObjectInArc (CSpaceObject *pSource, const CVector &vCenter, Metric rRange, const CSpaceObjectCriteria &Criteria, int iMinAngle, int iMaxAngle) const +CSpaceObject *CSystem::FindNearestObjectInArc (CSpaceObject *pSource, const CVector &vCenter, Metric rRange, + const std::function fnExclude, + const CSpaceObjectCriteria &Criteria, int iMinAngle, int iMaxAngle) const // FindNearestObject // @@ -1845,7 +1847,7 @@ CSpaceObject *CSystem::FindNearestTangibleObjectInArc (CSpaceObject *pSource, co CCriteriaObjSelector Selector(pSource, Criteria); CNearestInRadiusRange Range(vCenter, rRange); - return CSpaceObjectEnum::FindNearestTangibleObjInArc(*this, pSource, vCenter, iMinAngle, iMaxAngle, Range, Selector); + return CSpaceObjectEnum::FindNearestObjInArc(*this, pSource, vCenter, iMinAngle, iMaxAngle, fnExclude, Range, Selector); } // If we don't have a criteria, then we can do this faster. @@ -1855,7 +1857,7 @@ CSpaceObject *CSystem::FindNearestTangibleObjectInArc (CSpaceObject *pSource, co CAnyObjSelector Selector; CNearestInRadiusRange Range(vCenter, rRange); - return CSpaceObjectEnum::FindNearestTangibleObjInArc(*this, pSource, vCenter, iMinAngle, iMaxAngle, Range, Selector); + return CSpaceObjectEnum::FindNearestObjInArc(*this, pSource, vCenter, iMinAngle, iMaxAngle, fnExclude, Range, Selector); } } diff --git a/Mammoth/TSE/CWeaponTargetDefinition.cpp b/Mammoth/TSE/CWeaponTargetDefinition.cpp index 242359c3d..94447f09d 100644 --- a/Mammoth/TSE/CWeaponTargetDefinition.cpp +++ b/Mammoth/TSE/CWeaponTargetDefinition.cpp @@ -62,7 +62,7 @@ CSpaceObject* CWeaponTargetDefinition::FindTarget (CWeaponClass* pWeapon, CInsta // Now look for the nearest object - return pSystem->FindNearestTangibleObjectInArc(pSource, vSourcePos, rBestDist, m_TargetCriteria, iMinFireArc, iMaxFireArc); + return pSystem->FindNearestObjectInArc(pSource, vSourcePos, rBestDist, fnExclude, m_TargetCriteria, iMinFireArc, iMaxFireArc); } bool CWeaponTargetDefinition::AimAndFire(CWeaponClass* pWeapon, CInstalledDevice* pDevice, CSpaceObject* pSource, CDeviceClass::SDeviceUpdateCtx& Ctx) const