BVB Source Codes

CRYENGINE Show SmartObjects.cpp Source code

Return Download CRYENGINE: download SmartObjects.cpp Source code - Download CRYENGINE Source code - Type:.cpp
  1. // Copyright 2001-2016 Crytek GmbH / Crytek Group. All rights reserved.
  2.  
  3. /********************************************************************
  4.    -------------------------------------------------------------------------
  5.    File name:   SmartObjects.cpp
  6.    $Id$
  7.    Description:
  8.  
  9.    -------------------------------------------------------------------------
  10.    History:
  11.    - ?                                          : Created by ?
  12.  
  13.  *********************************************************************/
  14. #include "StdAfx.h"
  15. #include <CrySystem/ISystem.h>
  16. #include <CrySystem/XML/IXml.h>
  17. #include <CryAnimation/ICryAnimation.h>
  18. #include <CrySystem/IConsole.h>
  19.  
  20. #include "AIObject.h"
  21. #include "PipeUser.h"
  22. #include "CAISystem.h"
  23. #include "DebugDrawContext.h"
  24.  
  25. #include "AIActions.h"
  26. #include "SmartObjects.h"
  27.  
  28. #include "Navigation/NavigationSystem/NavigationSystem.h"
  29. #include "Navigation/MNM/MNM.h"
  30.  
  31. CSmartObject::CState::MapSmartObjectStateIds CSmartObject::CState::g_mapStateIds;
  32. CSmartObject::CState::MapSmartObjectStates CSmartObject::CState::g_mapStates;
  33. CSmartObject::SetStates CSmartObject::CState::g_defaultStates;
  34.  
  35. CSmartObjectClass::MapClassesByName CSmartObjectClass::g_AllByName;
  36.  
  37. CSmartObjectClass::VectorClasses CSmartObjectClass::g_AllUserClasses;
  38. CSmartObjectClass::VectorClasses::iterator CSmartObjectClass::g_itAllUserClasses = CSmartObjectClass::g_AllUserClasses.end();
  39.  
  40. CSmartObjectManager::SmartObjects CSmartObjectManager::g_AllSmartObjects;
  41. std::map<EntityId, CSmartObject*> CSmartObjectManager::g_smartObjectEntityMap;
  42.  
  43. IMaterial* CClassTemplateData::m_pHelperMtl = NULL;
  44.  
  45. const static char* const g_sEmptyNavSO = "emptynavso";
  46. const static float EMPTY_SO_USE_DISTANCE = 5.0f;
  47.  
  48. // this mask should be used for finding enclosing node of navigation smart objects
  49. #define SMART_OBJECT_ENCLOSING_NAV_TYPES (IAISystem::NAV_TRIANGULAR | IAISystem::NAV_WAYPOINT_HUMAN | IAISystem::NAV_WAYPOINT_3DSURFACE | IAISystem::NAV_VOLUME)
  50.  
  51. const CSmartObject::SetStates& CSmartObject::CState::GetDefaultStates()
  52. {
  53.         if (g_defaultStates.empty())
  54.         {
  55.                 g_defaultStates.insert(CSmartObject::CState("Idle"));
  56.         }
  57.  
  58.         return g_defaultStates;
  59. }
  60.  
  61. bool CCondition::operator==(const CCondition& other) const
  62. {
  63. #define IfDiffReturnFalse(X)     if (X != other.X) return false;
  64. #define ElseIfDiffReturnFalse(X) else if (X != other.X) \
  65.   return false;
  66.  
  67.         IfDiffReturnFalse(iTemplateId)
  68.         ElseIfDiffReturnFalse(pUserClass)
  69.         ElseIfDiffReturnFalse(userStatePattern)
  70.         ElseIfDiffReturnFalse(pObjectClass)
  71.         ElseIfDiffReturnFalse(objectStatePattern)
  72.         ElseIfDiffReturnFalse(sObjectHelper)
  73.         ElseIfDiffReturnFalse(sUserHelper)
  74.         ElseIfDiffReturnFalse(fDistanceFrom)
  75.         ElseIfDiffReturnFalse(fDistanceTo)
  76.         ElseIfDiffReturnFalse(fOrientationLimit)
  77.         ElseIfDiffReturnFalse(bHorizLimitOnly)
  78.         ElseIfDiffReturnFalse(fOrientationToTargetLimit)
  79.         ElseIfDiffReturnFalse(fMinDelay)
  80.         ElseIfDiffReturnFalse(fMaxDelay)
  81.         ElseIfDiffReturnFalse(fMemory)
  82.         ElseIfDiffReturnFalse(fProximityFactor)
  83.         ElseIfDiffReturnFalse(fOrientationFactor)
  84.         ElseIfDiffReturnFalse(fVisibilityFactor)
  85.         ElseIfDiffReturnFalse(fRandomnessFactor)
  86.         ElseIfDiffReturnFalse(fLookAtPerc)
  87.         ElseIfDiffReturnFalse(userPostActionStates)
  88.         ElseIfDiffReturnFalse(objectPostActionStates)
  89.         ElseIfDiffReturnFalse(eActionType)
  90.         ElseIfDiffReturnFalse(sAction)
  91.         ElseIfDiffReturnFalse(userPreActionStates)
  92.         ElseIfDiffReturnFalse(objectPreActionStates)
  93.         ElseIfDiffReturnFalse(iMaxAlertness)
  94.         ElseIfDiffReturnFalse(bEnabled)
  95.         ElseIfDiffReturnFalse(sEvent)
  96.         ElseIfDiffReturnFalse(sChainedUserEvent)
  97.         ElseIfDiffReturnFalse(sChainedObjectEvent)
  98.         ElseIfDiffReturnFalse(sName)
  99.         ElseIfDiffReturnFalse(sDescription)
  100.         ElseIfDiffReturnFalse(sFolder)
  101.         ElseIfDiffReturnFalse(iOrder)
  102.         ElseIfDiffReturnFalse(iRuleType)
  103.         ElseIfDiffReturnFalse(sEntranceHelper)
  104.         ElseIfDiffReturnFalse(sExitHelper)
  105.         ElseIfDiffReturnFalse(fApproachSpeed)
  106.         ElseIfDiffReturnFalse(iApproachStance)
  107.         ElseIfDiffReturnFalse(sAnimationHelper)
  108.         ElseIfDiffReturnFalse(sApproachHelper)
  109.         ElseIfDiffReturnFalse(fStartWidth)
  110.         ElseIfDiffReturnFalse(fDirectionTolerance)
  111.         ElseIfDiffReturnFalse(fStartArcAngle)
  112.         else
  113.                 return true;
  114.  
  115. #undef IfDiffReturnFalse
  116. #undef ElseIfDiffReturnFalse
  117. }
  118.  
  119. /////////////////////////////////////////
  120. // CSmartObjectBase class implementation
  121. /////////////////////////////////////////
  122.  
  123. Vec3 CSmartObjectBase::GetPos() const
  124. {
  125.         IEntity* pEntity = GetEntity();
  126.         AIAssert(pEntity);
  127.  
  128.         if (pEntity)
  129.         {
  130.                 IAIObject* pAI = pEntity->GetAI();
  131.                 const bool isAIEnabled = pAI ? pAI->IsEnabled() : false;
  132.                 if (isAIEnabled)
  133.                 {
  134.                         return pAI->GetPos();
  135.                 }
  136.  
  137.                 return pEntity->GetWorldPos();
  138.         }
  139.  
  140.         return Vec3Constants<float>::fVec3_Zero;
  141. }
  142.  
  143. bool GetPelvisJointForRagdollizedActor(IEntity* pEntity, QuatT& pelvisJoint)
  144. {
  145.         IPhysicalEntity* pPE = pEntity->GetPhysics();
  146.         const bool isArticulatedEntity = pPE ? (pPE->GetType() == PE_ARTICULATED) : false;
  147.         if (isArticulatedEntity && pEntity->HasAI())
  148.         {
  149.                 // it's a ragdollized actor -> special processing
  150.                 ICharacterInstance* pCharInstance = pEntity->GetCharacter(0);
  151.                 if (pCharInstance)
  152.                 {
  153.                         const IDefaultSkeleton& rIDefaultSkeleton = pCharInstance->GetIDefaultSkeleton();
  154.                         int boneId = rIDefaultSkeleton.GetJointIDByName("Bip01 Pelvis");
  155.                         if (boneId >= 0)
  156.                         {
  157.                                 ISkeletonPose* pSkeletonPose = pCharInstance->GetISkeletonPose();
  158.                                 pelvisJoint = pSkeletonPose->GetAbsJointByID(boneId);
  159.                                 return true;
  160.                         }
  161.                 }
  162.         }
  163.  
  164.         return false;
  165. }
  166.  
  167. Vec3 CSmartObjectBase::GetHelperPos(const SmartObjectHelper* pHelper) const
  168. {
  169.         IEntity* pEntity = GetEntity();
  170.         AIAssert(pEntity);
  171.         if (pEntity)
  172.         {
  173.                 QuatT pelvisJoint;
  174.                 if (GetPelvisJointForRagdollizedActor(pEntity, pelvisJoint))
  175.                 {
  176.                         Vec3 pos(pHelper->qt.t.z, -pHelper->qt.t.y, pHelper->qt.t.x);
  177.                         pos = pelvisJoint * pos;
  178.                         return pEntity->GetWorldTM().TransformPoint(pos);
  179.                 }
  180.  
  181.                 return pEntity->GetWorldTM().TransformPoint(pHelper->qt.t);
  182.         }
  183.  
  184.         return pHelper->qt.t;
  185. }
  186.  
  187. Vec3 CSmartObjectBase::GetOrientation(const SmartObjectHelper* pHelper) const
  188. {
  189.         IEntity* pEntity = GetEntity();
  190.         AIAssert(pEntity);
  191.         if (!pEntity)
  192.         {
  193.                 return pHelper ? pHelper->qt.q * FORWARD_DIRECTION : FORWARD_DIRECTION;
  194.         }
  195.  
  196.         QuatT pelvisJoint;
  197.         if (GetPelvisJointForRagdollizedActor(pEntity, pelvisJoint))
  198.         {
  199.                 Vec3 forward = pHelper ? pHelper->qt.q * FORWARD_DIRECTION : FORWARD_DIRECTION;
  200.                 forward.Set(forward.z, -forward.y, forward.x);
  201.                 forward = pelvisJoint.q * forward;
  202.                 return pEntity->GetWorldTM().TransformVector(forward);
  203.         }
  204.  
  205.         if (pHelper)
  206.                 return pEntity->GetWorldTM().TransformVector(pHelper->qt.q * FORWARD_DIRECTION);
  207.  
  208.         CAIActor* pAIActor = CastToCAIActorSafe(pEntity->GetAI());
  209.         if (pAIActor)
  210.         {
  211.                 const Vec3& vMoveDir = pAIActor->GetState().vMoveDir;
  212.                 return (vMoveDir.IsZero(0.1f)) ? pAIActor->GetViewDir() : vMoveDir;
  213.         }
  214.  
  215.         return pEntity->GetWorldTM().TransformVector(Vec3Constants<float>::fVec3_OneY);
  216. }
  217.  
  218. CPipeUser* CSmartObjectBase::GetPipeUser() const
  219. {
  220.         return CastToCPipeUserSafe(GetAI());
  221. }
  222.  
  223. CAIActor* CSmartObjectBase::GetAIActor() const
  224. {
  225.         return CastToCAIActorSafe(GetAI());
  226. }
  227.  
  228. void CSmartObjectBase::OnReused(IEntity* pEntity)
  229. {
  230.         m_entityId = pEntity ? pEntity->GetId() : 0;
  231. }
  232.  
  233. /////////////////////////////////////////
  234. // CSmartObject class implementation
  235. /////////////////////////////////////////
  236.  
  237. CSmartObject::CSmartObject(EntityId entityId)
  238.         : CSmartObjectBase(entityId)
  239.         , m_States(CState::GetDefaultStates())
  240.         , m_fLookAtLimit(0.0f)
  241.         , m_vLookAtPos(ZERO)
  242.         , m_eValidationResult(eSOV_Unknown)
  243.         , m_bHidden(false)
  244. {
  245.         m_fRandom = cry_random(0.0f, 0.5f);
  246.  
  247.         Register();
  248. }
  249.  
  250. CSmartObject::~CSmartObject()
  251. {
  252.         CSmartObjectManager::g_AllSmartObjects.erase(this);
  253.         UnregisterFromAllClasses();
  254.  
  255.         CSmartObjectManager::RemoveSmartObjectFromEntity(m_entityId, this);
  256. }
  257.  
  258. void CSmartObject::OnReused(IEntity* pEntity)
  259. {
  260.         CSmartObjectManager::RemoveSmartObjectFromEntity(m_entityId, this);
  261.  
  262.         AIAssert(pEntity);
  263.  
  264.         CSmartObjectManager::g_AllSmartObjects.erase(this);
  265.         CSmartObjectBase::OnReused(pEntity);
  266.         AIAssert(pEntity == GetEntity());
  267.  
  268.         Reset();
  269.  
  270.         UnregisterFromAllClasses();
  271.         m_enclosingNavNodes.clear();
  272.         m_correspondingNavNodes.clear();
  273.         m_navLinks.clear();
  274.  
  275.         m_States.clear();
  276.         m_States.insert(CState("Idle"));
  277.  
  278.         m_fRandom = cry_random(0.0f, 0.5f);
  279.         m_fLookAtLimit = 0.0f;
  280.         m_vLookAtPos.zero();
  281.         m_eValidationResult = eSOV_Unknown;
  282.         m_bHidden = false;
  283.  
  284.         Register();
  285. }
  286.  
  287. void CSmartObject::Register()
  288. {
  289.         IEntity* pEntity = GetEntity();
  290.         AIAssert(pEntity);
  291.  
  292.         EntityId entityId = pEntity ? pEntity->GetId() : 0;
  293.         if (entityId > 0)
  294.         {
  295.                 AIAssert(CSmartObjectManager::g_AllSmartObjects.find(this) == CSmartObjectManager::g_AllSmartObjects.end());
  296.                 CSmartObjectManager::g_AllSmartObjects.insert(this);
  297.                 CSmartObjectManager::BindSmartObjectToEntity(entityId, this);
  298.         }
  299. }
  300.  
  301. void CSmartObject::Serialize(TSerialize ser)
  302. {
  303.         if (ser.IsReading())
  304.         {
  305.                 m_Events.clear();
  306.                 m_mapLastUpdateTimes.clear();
  307.                 m_fLookAtLimit = 0;
  308.                 m_vLookAtPos.zero();
  309.                 m_enclosingNavNodes.clear();
  310.                 m_correspondingNavNodes.clear();
  311.                 m_navLinks.clear();
  312.         }
  313.  
  314.         ser.Value("m_fRandom", m_fRandom);
  315.  
  316.         const SetStates& defaultStates = CState::GetDefaultStates();
  317.         if (ser.BeginOptionalGroup("States", m_States != defaultStates))
  318.         {
  319.                 ser.Value("States", m_States);
  320.                 ser.EndGroup();
  321.         }
  322.         else if (ser.IsReading())
  323.         {
  324.                 m_States = defaultStates;
  325.         }
  326. }
  327.  
  328. void CSmartObject::ApplyUserSize()
  329. {
  330.         Vec3 size = MeasureUserSize();
  331.         size.y += size.z;
  332.  
  333.         //Exit early if this object has no size
  334.         if (size.IsZero())
  335.                 return;
  336.  
  337.         for (uint32 i = 0; i < m_vClasses.size(); ++i)
  338.         {
  339.                 CSmartObjectClass* pClass = m_vClasses[i];
  340.  
  341.                 CSmartObjectClass::UserSize userSize(size.x, size.y, size.z);
  342.                 pClass->m_StanceMaxSize += userSize;
  343.         }
  344. }
  345.  
  346. void CSmartObjectManager::SerializePointer(TSerialize ser, const char* name, CSmartObject*& pSmartObject)
  347. {
  348.         if (ser.IsWriting())
  349.         {
  350.                 EntityId id = pSmartObject ? pSmartObject->GetEntityId() : 0;
  351.                 ser.Value(name, id);
  352.         }
  353.         else
  354.         {
  355.                 EntityId id;
  356.                 ser.Value(name, id);
  357.                 if (id)
  358.                 {
  359.                         pSmartObject = GetSmartObject(id);
  360.                         AIAssert(pSmartObject);
  361.                 }
  362.                 else
  363.                         pSmartObject = NULL;
  364.         }
  365. }
  366.  
  367. void CSmartObjectManager::SerializePointer(TSerialize ser, const char* name, CCondition*& pRule)
  368. {
  369.         if (ser.IsWriting())
  370.         {
  371.                 if (pRule)
  372.                         ser.Value(name, pRule->iOrder);
  373.                 else
  374.                 {
  375.                         int nulValue = -1;
  376.                         ser.Value(name, nulValue);
  377.                 }
  378.         }
  379.         else
  380.         {
  381.                 pRule = NULL;
  382.                 int iOrder;
  383.                 ser.Value(name, iOrder);
  384.                 if (iOrder == -1)
  385.                         return;
  386.  
  387.                 MapConditions& rules = gAIEnv.pSmartObjectManager->m_Conditions;
  388.                 MapConditions::iterator it, itEnd = rules.end();
  389.                 for (it = rules.begin(); it != itEnd; ++it)
  390.                         if (it->second.iOrder == iOrder)
  391.                         {
  392.                                 pRule = &it->second;
  393.                                 return;
  394.                         }
  395.         }
  396. }
  397.  
  398. void CSmartObject::Use(CSmartObject* pObject, CCondition* pCondition, int eventId /*=0*/, bool bForceHighPriority /*=false*/) const
  399. {
  400.         CAIActionManager* pActionManager = gAIEnv.pAIActionManager;
  401.         const char* sSignal = NULL;
  402.         IAIAction* pAction = NULL;
  403.         if (!pCondition->sAction.empty() && pCondition->eActionType != eAT_None)
  404.         {
  405.                 if (pCondition->eActionType == eAT_AISignal)
  406.                         sSignal = pCondition->sAction;
  407.                 else
  408.                         pAction = pActionManager->GetAIAction(pCondition);
  409.  
  410.                 if (!pAction && !sSignal)
  411.                 {
  412.                         AIWarning("Undefined AI action \"%s\"!", pCondition->sAction.c_str());
  413.  
  414.                         // undo states since action wasn't found
  415.                         if (!pCondition->userPreActionStates.empty())
  416.                                 gAIEnv.pSmartObjectManager->ModifySmartObjectStates(GetEntity(), pCondition->userPreActionStates.AsUndoString());
  417.                         if (this != pObject)
  418.                                 if (!pCondition->objectPreActionStates.empty())
  419.                                         gAIEnv.pSmartObjectManager->ModifySmartObjectStates(pObject->GetEntity(), pCondition->objectPreActionStates.AsUndoString());
  420.                         return;
  421.                 }
  422.         }
  423.  
  424.         int maxAlertness = pCondition->iMaxAlertness;
  425.         if (bForceHighPriority || pCondition->eActionType == eAT_PriorityAction || pCondition->eActionType == eAT_PriorityAnimationSignal || pCondition->eActionType == eAT_PriorityAnimationAction)
  426.                 maxAlertness += 100;
  427.  
  428.         CAIActor* pAIObject = GetAIActor();
  429.         const bool isAIAgent = pAIObject ? pAIObject->IsAgent() : false;
  430.         if (isAIAgent)
  431.         {
  432.                 IEntity* pObjectEntity = pObject->GetEntity();
  433.                 if (sSignal)
  434.                 {
  435.                         AISignalExtraData* pExtraData = new AISignalExtraData;
  436.                         if (eventId)
  437.                                 pExtraData->iValue = eventId;
  438.                         pExtraData->nID = pObject->GetEntityId();
  439.  
  440.                         pExtraData->point = pCondition->pObjectHelper ? pObject->GetHelperPos(pCondition->pObjectHelper) : pObject->GetPos();
  441.  
  442.                         pAIObject->SetSignal(10, sSignal, pObjectEntity, pExtraData);
  443.  
  444.                         // update states
  445.                         if (!pCondition->userPostActionStates.empty())   // check is next state non-empty
  446.                                 gAIEnv.pSmartObjectManager->ModifySmartObjectStates(GetEntity(), pCondition->userPostActionStates.AsString());
  447.                         if (this != pObject)
  448.                                 if (!pCondition->objectPostActionStates.empty())   // check is next state non-empty
  449.                                         gAIEnv.pSmartObjectManager->ModifySmartObjectStates(pObjectEntity, pCondition->objectPostActionStates.AsString());
  450.  
  451.                         return;
  452.                 }
  453.                 //else if (pAIObject->CastToCAIPlayer())
  454.                 //{
  455.                 //      pAIObject = NULL;
  456.                 //}
  457.                 else
  458.                 {
  459.                         PREFAST_ASSUME(pAction);
  460.  
  461.                         if (eventId || (pAction && !pAction->GetFlowGraph()))
  462.                         {
  463.                                 if (!pAction->GetFlowGraph())
  464.                                 {
  465.                                         // set reference point to say where to play the animation
  466.                                         CAnimationAction* pAnimAction = static_cast<CAnimationAction*>(pAction);
  467.  
  468.                                         if (pCondition->pObjectClass)
  469.                                         {
  470.                                                 if (pCondition->sAnimationHelper.c_str()[0] == '<')
  471.                                                 {
  472.                                                         if (pCondition->sAnimationHelper == "<Auto>")
  473.                                                                 pAnimAction->SetAutoTarget(pObject->GetPos(), pObject->GetOrientation(NULL));
  474.                                                         else if (pCondition->sAnimationHelper == "<AutoInverse>")
  475.                                                                 pAnimAction->SetAutoTarget(pObject->GetPos(), Quat::CreateRotationZ(gf_PI) * pObject->GetOrientation(NULL));
  476.                                                         else
  477.                                                                 AIError("Invalid animation helper name '%s' in smart object rule '%s'!", pCondition->sAnimationHelper.c_str(), pCondition->sName.c_str());
  478.  
  479.                                                         // approach target might be different
  480.                                                         const SmartObjectHelper* pApproachHelper = pCondition->pObjectClass->GetHelper(pCondition->sApproachHelper);
  481.                                                         if (pApproachHelper)
  482.                                                                 pAnimAction->SetApproachPos(pObject->GetHelperPos(pApproachHelper));
  483.                                                 }
  484.                                                 else if (const SmartObjectHelper* pHelper = pCondition->pObjectClass->GetHelper(pCondition->sAnimationHelper))
  485.                                                 {
  486.                                                         pAnimAction->SetTarget(pObject->GetHelperPos(pHelper), pObject->GetOrientation(pHelper));
  487.  
  488.                                                         // approach target might be different
  489.                                                         const SmartObjectHelper* pApproachHelper = pCondition->pObjectClass->GetHelper(pCondition->sApproachHelper);
  490.                                                         if (pApproachHelper)
  491.                                                                 pAnimAction->SetApproachPos(pObject->GetHelperPos(pApproachHelper));
  492.                                                 }
  493.                                                 else
  494.                                                 {
  495.                                                         Vec3 direction = pObject->GetPos() - GetPos();
  496.                                                         direction.NormalizeSafe();
  497.                                                         pAnimAction->SetTarget(GetPos(), direction);
  498.                                                 }
  499.                                         }
  500.                                         else
  501.                                         {
  502.                                                 pAnimAction->SetTarget(GetPos(), GetOrientation(NULL));
  503.                                         }
  504.                                 }
  505.  
  506.                                 pActionManager->ExecuteAIAction(pAction, GetEntity(), pObject->GetEntity(), maxAlertness, eventId,
  507.                                                                 pCondition->userPostActionStates.AsString(), pCondition->objectPostActionStates.AsString(),
  508.                                                                 pCondition->userPreActionStates.AsUndoString(), pCondition->objectPreActionStates.AsUndoString());
  509.                         }
  510.                         else
  511.                         {
  512.                                 AISignalExtraData* pExtraData = new AISignalExtraData;
  513.                                 pExtraData->SetObjectName(string(pAction->GetName()) +
  514.                                                           ",\"" + pCondition->userPostActionStates.AsString() + '\"' +
  515.                                                           ",\"" + pCondition->objectPostActionStates.AsString() + '\"' +
  516.                                                           ",\"" + pCondition->userPreActionStates.AsUndoString() + '\"' +
  517.                                                           ",\"" + pCondition->objectPreActionStates.AsUndoString() + '\"');
  518.                                 pExtraData->iValue = maxAlertness;
  519.                                 pAIObject->SetSignal(10, "OnUseSmartObject", pObjectEntity, pExtraData, gAIEnv.SignalCRCs.m_nOnUseSmartObject);
  520.                         }
  521.  
  522.                         return;
  523.                 }
  524.         }
  525.  
  526.         if (!pAIObject)
  527.         {
  528.                 // the user isn't an AI Actor / Pipe User / Puppet / Vehicle / Player
  529.                 if (sSignal)
  530.                 {
  531.                         AIWarning("Attempt to send AI signal to an entity not registered as "
  532.                                   "AI Actor / Pipe User / Puppet / Vehicle / Player in AI system!");
  533.  
  534.                         // undo pre-action states
  535.                         if (!pCondition->userPreActionStates.empty())   // check is next state non-empty
  536.                                 gAIEnv.pSmartObjectManager->ModifySmartObjectStates(GetEntity(), pCondition->userPreActionStates.AsUndoString());
  537.                         if (this != pObject)
  538.                                 if (!pCondition->objectPreActionStates.empty())   // check is next state non-empty
  539.                                         gAIEnv.pSmartObjectManager->ModifySmartObjectStates(pObject->GetEntity(), pCondition->objectPreActionStates.AsUndoString());
  540.                 }
  541.                 else if (pAction && pAction->GetFlowGraph())
  542.                 {
  543.                         gAIEnv.pAIActionManager->ExecuteAIAction(pAction, GetEntity(), pObject->GetEntity(), 100, eventId,
  544.                                                                  pCondition->userPostActionStates.AsString(), pCondition->objectPostActionStates.AsString(),
  545.                                                                  pCondition->userPreActionStates.AsUndoString(), pCondition->objectPreActionStates.AsUndoString());
  546.                 }
  547.         }
  548. }
  549.  
  550. /////////////////////////////////////////
  551. // CSmartObjectClass class implementation
  552. /////////////////////////////////////////
  553.  
  554. void CSmartObjectClass::RegisterSmartObject(CSmartObject* pSmartObject)
  555. {
  556.         AIAssert(std::find(m_allSmartObjectInstances.begin(), m_allSmartObjectInstances.end(), pSmartObject) == m_allSmartObjectInstances.end());
  557.         m_allSmartObjectInstances.push_back(pSmartObject);
  558.         pSmartObject->RegisterSmartObjectClass(this);
  559. }
  560.  
  561. void CSmartObjectClass::UnregisterSmartObject(CSmartObject* pSmartObject)
  562. {
  563.         AIAssert(pSmartObject);
  564.  
  565.         const bool foundSmartObject = RemoveSmartObject(pSmartObject, false);
  566.         AIAssert(foundSmartObject);
  567. }
  568.  
  569. bool CSmartObjectClass::RemoveSmartObject(CSmartObject* pSmartObject, bool bCanDelete)
  570. {
  571.         AIAssert(pSmartObject);
  572.  
  573.         // Find registered SO
  574.         const bool foundSmartObject = stl::find_and_erase(m_allSmartObjectInstances, pSmartObject);
  575.         if (foundSmartObject)
  576.         {
  577.                 pSmartObject->UnregisterSmartObjectClass(this);
  578.  
  579.                 RemoveFromPositionMap(pSmartObject);
  580.  
  581.                 // If last class reference has just been deleted
  582.                 if (pSmartObject->GetClasses().empty() && bCanDelete)
  583.                         delete pSmartObject;
  584.         }
  585.  
  586.         return foundSmartObject;
  587. }
  588.  
  589. void CSmartObjectClass::RemoveFromPositionMap(CSmartObject* pSmartObject)
  590. {
  591.         // Remove position cache for SO (can't trust position, so iterate through all objects)
  592.         MapSmartObjectsByPos::iterator itByPos, itByPosEnd = m_MapObjectsByPos.end();
  593.         for (itByPos = m_MapObjectsByPos.begin(); itByPos != itByPosEnd; ++itByPos)
  594.         {
  595.                 CSmartObject* pCurrentObject = itByPos->second;
  596.                 if (pCurrentObject == pSmartObject)
  597.                 {
  598.                         m_MapObjectsByPos.erase(itByPos);
  599.                         break;
  600.                 }
  601.         }
  602. }
  603.  
  604. CSmartObjectClass::CSmartObjectClass(const char* className)
  605.         : m_sName(className)
  606.         , m_bSmartObjectUser(false)
  607.         , m_bNeeded(false)
  608.         , m_pTemplateData(NULL)
  609.         , m_indexNextObject(~0)
  610. {
  611.         g_AllByName[className] = this;
  612. }
  613.  
  614. CSmartObjectClass::~CSmartObjectClass()
  615. {
  616.         while (!m_allSmartObjectInstances.empty())
  617.         {
  618.                 AIAssert(m_allSmartObjectInstances.back());
  619.                 RemoveSmartObject(m_allSmartObjectInstances.back(), true);
  620.         }
  621.  
  622.         VectorClasses::iterator it = std::find(g_AllUserClasses.begin(), g_AllUserClasses.end(), this);
  623.         if (it != g_AllUserClasses.end())
  624.         {
  625.                 // erase unordered vector element
  626.                 *it = g_AllUserClasses.back();
  627.                 g_AllUserClasses.pop_back();
  628.         }
  629.         g_itAllUserClasses = CSmartObjectClass::g_AllUserClasses.end();
  630.  
  631.         g_AllByName.erase(m_sName);
  632. }
  633.  
  634. CSmartObjectClass* CSmartObjectClass::find(const char* sClassName)
  635. {
  636.         if (g_AllByName.empty())
  637.                 return NULL;
  638.         MapClassesByName::iterator it = g_AllByName.find(CONST_TEMP_STRING(sClassName));
  639.         return it != g_AllByName.end() ? it->second : NULL;
  640. }
  641.  
  642. void CSmartObjectClass::ClearHelperLinks()
  643. {
  644.         m_setNavHelpers.clear();
  645.         m_vHelperLinks.clear();
  646. }
  647.  
  648. bool CSmartObjectClass::AddHelperLink(CCondition* condition)
  649. {
  650.         AIAssert(condition->pObjectClass == this);
  651.         SmartObjectHelper* pEntrance = GetHelper(condition->sEntranceHelper);
  652.         SmartObjectHelper* pExit = GetHelper(condition->sExitHelper);
  653.         if (pEntrance && pExit)
  654.         {
  655.                 m_setNavHelpers.insert(pEntrance);
  656.                 m_setNavHelpers.insert(pExit);
  657.  
  658.                 HelperLink link = {
  659.                         condition->iRuleType == 1 ? 100.f : 0.f,
  660.                         pEntrance,
  661.                         pExit,
  662.                         condition
  663.                 };
  664.                 m_vHelperLinks.push_back(link);
  665.                 return true;
  666.         }
  667.         return false;
  668. }
  669.  
  670. int CSmartObjectClass::FindHelperLinks(
  671.   const SmartObjectHelper* from, const SmartObjectHelper* to, const CSmartObjectClass* pClass, float radius,
  672.   CSmartObjectClass::HelperLink** pvHelperLinks, int iCount /*=1*/)
  673. {
  674.         int numFound = 0;
  675.         int i = m_vHelperLinks.size();
  676.         while (i-- && numFound < iCount)
  677.         {
  678.                 HelperLink* pLink = &m_vHelperLinks[i];
  679.                 if (pLink->from == from && pLink->to == to && pClass == pLink->condition->pUserClass && radius <= pLink->passRadius)
  680.                         pvHelperLinks[numFound++] = pLink;
  681.         }
  682.         return numFound;
  683. }
  684.  
  685. int CSmartObjectClass::FindHelperLinks(
  686.   const SmartObjectHelper* from, const SmartObjectHelper* to, const CSmartObjectClass* pClass, float radius,
  687.   const CSmartObject::SetStates& userStates, const CSmartObject::SetStates& objectStates,
  688.   CSmartObjectClass::HelperLink** pvHelperLinks, int iCount /*=1*/, const CSmartObject::SetStates* pObjectStatesToExcludeFromMatches /* = NULL*/)
  689. {
  690.         int numFound = 0;
  691.         int i = m_vHelperLinks.size();
  692.         while (i-- && numFound < iCount)
  693.         {
  694.                 HelperLink* pLink = &m_vHelperLinks[i];
  695.                 if (pLink->from == from && pLink->to == to && pClass == pLink->condition->pUserClass && radius <= pLink->passRadius &&
  696.                     pLink->condition->userStatePattern.Matches(userStates) && pLink->condition->objectStatePattern.Matches(objectStates, pObjectStatesToExcludeFromMatches))
  697.                         pvHelperLinks[numFound++] = pLink;
  698.         }
  699.         return numFound;
  700. }
  701.  
  702. /////////////////////////////////////////
  703. // CSmartObjectManager class implementation
  704. /////////////////////////////////////////
  705.  
  706. typedef std::list<string> ListTokens;
  707.  
  708. int Tokenize(char*& str, int& length)
  709. {
  710.         char* token = str;
  711.         int tokenLength = length;
  712.  
  713.         // Skip the previous token.
  714.         token += tokenLength;
  715.  
  716.         // Skip any leading whitespace.
  717.         while (*token && !((*token >= 'A' && *token <= 'Z') || (*token >= 'a' && *token <= 'z') || *token == '_'))
  718.                 ++token;
  719.  
  720.         // Find the end of the token.
  721.         tokenLength = 0;
  722.         char* end = token;
  723.         while ((*end >= '0' && *end <= '9') || (*end >= 'A' && *end <= 'Z') || (*end >= 'a' && *end <= 'z') || *end == '_')
  724.                 ++end;
  725.         tokenLength = (int)(end - token);
  726.  
  727.         str = token;
  728.         length = tokenLength;
  729.  
  730.         return length;
  731. }
  732.  
  733. void CSmartObjectManager::String2Classes(const string& sClass, CSmartObjectClasses& vClasses)
  734. {
  735.         CSmartObjectClass* pClass;
  736.  
  737.         // Make a temporary copy of the string - more efficient to do this once without memory allocations,
  738.         // because then we can write temporary null-terminators into the buffer to avoid copying/string allocations
  739.         // later.
  740.         char classString[1024];
  741.         AIAssert(int(sClass.size()) < sizeof(classString) - 1);
  742.         int stringLength = min(int(sClass.size()), int(sizeof(classString)) - 1);
  743.         memcpy(classString, sClass.c_str(), stringLength);
  744.         classString[stringLength] = 0;
  745.  
  746.         char* token = classString;
  747.         int tokenLength = 0;
  748.         while (Tokenize(token, tokenLength))
  749.         {
  750.                 // Temporarily null-terminate the token.
  751.                 char oldChar = token[tokenLength];
  752.                 token[tokenLength] = 0;
  753.  
  754.                 pClass = GetSmartObjectClass(token);
  755.  
  756.                 if (pClass && std::find(vClasses.begin(), vClasses.end(), pClass) == vClasses.end())
  757.                         vClasses.push_back(pClass);
  758.  
  759.                 token[tokenLength] = oldChar;
  760.         }
  761.         ;
  762. }
  763.  
  764. void CSmartObjectManager::String2States(const char* listStates, CSmartObject::DoubleVectorStates& states)
  765. {
  766.         states.positive.clear();
  767.         states.negative.clear();
  768.  
  769.         // Make a temporary copy of the string - more efficient to do this once without memory allocations,
  770.         // because then we can write temporary null-terminators into the buffer to avoid copying/string allocations
  771.         // later.
  772.         char statesString[1024];
  773.         int inputStringLength = int(strlen(listStates));
  774.         int stringLength = min(inputStringLength, int(sizeof(statesString)) - 1);
  775.         AIAssert(inputStringLength < sizeof(statesString) - 1);
  776.         memcpy(statesString, listStates, stringLength);
  777.         statesString[stringLength] = 0;
  778.  
  779.         // Split the string into positive and negative state lists.
  780.         int hyphenPosition = -1;
  781.         for (int i = 0; statesString[i]; ++i)
  782.         {
  783.                 if (statesString[i] == '-')
  784.                 {
  785.                         hyphenPosition = i;
  786.                         break;
  787.                 }
  788.         }
  789.  
  790.         char* positiveStateList = statesString;
  791.         char* negativeStateList = 0;
  792.         if (hyphenPosition >= 0)
  793.         {
  794.                 statesString[hyphenPosition] = 0;
  795.                 negativeStateList = statesString + hyphenPosition + 1;
  796.         }
  797.  
  798.         // Examine the two lists (positive and negative) to extract a list of states for each.
  799.         CSmartObject::VectorStates* stateVectors[2] = { &states.positive, &states.negative };
  800.         char* stateStrings[2] = { positiveStateList, negativeStateList };
  801.  
  802.         for (int stateVectorIndex = 0; stateVectorIndex < 2; ++stateVectorIndex)
  803.         {
  804.                 char* token = stateStrings[stateVectorIndex];
  805.                 int tokenLength = 0;
  806.                 while (token && Tokenize(token, tokenLength))
  807.                 {
  808.                         // Temporarily null-terminate the token.
  809.                         char oldChar = token[tokenLength];
  810.                         token[tokenLength] = 0;
  811.  
  812.                         stateVectors[stateVectorIndex]->push_back(CSmartObject::CState(token));
  813.  
  814.                         token[tokenLength] = oldChar;
  815.                 }
  816.         }
  817. }
  818.  
  819. void CSmartObjectManager::String2StatePattern(const char* sPattern, CSmartObject::CStatePattern& pattern)
  820. {
  821.         pattern.clear();
  822.         string item;
  823.         CSmartObject::DoubleVectorStates dvs;
  824.  
  825.         while (sPattern)
  826.         {
  827.                 const char* i = strchr(sPattern, '|');
  828.                 if (!i)
  829.                 {
  830.                         item = sPattern;
  831.                 }
  832.                 else
  833.                 {
  834.                         item.assign(sPattern, i - sPattern);
  835.                         ++i;
  836.                 }
  837.  
  838.                 String2States(item, dvs);
  839.  
  840.                 if (!dvs.empty())
  841.                         pattern.push_back(dvs);
  842.  
  843.                 sPattern = i;
  844.         }
  845. }
  846.  
  847. CSmartObjectManager::CSmartObjectManager()
  848.         : m_pPreOnSpawnEntity(NULL),
  849.         m_initialized(false)
  850. {
  851.         // Register system states. Can only be done after CState::Reset() or ID's are lost.
  852.         CSmartObject::CState::Reset();
  853.         m_StateSameGroup = CSmartObject::CState("SameGroup");
  854.         m_StateSameFaction = CSmartObject::CState("SameFaction");
  855.         m_StateAttTarget = CSmartObject::CState("AttTarget");
  856.         m_StateBusy = CSmartObject::CState("Busy");
  857.  
  858.         m_statesToExcludeForPathfinding.insert(m_StateBusy);
  859.  
  860.         CSmartObject::CState("Idle");
  861.         CSmartObject::CState("Alerted");
  862.         CSmartObject::CState("Combat");
  863.         CSmartObject::CState("Dead");
  864.  
  865.         uint64 onEventSubscriptions = 0;
  866.         onEventSubscriptions |= ENTITY_EVENT_BIT(ENTITY_EVENT_INIT);
  867.         onEventSubscriptions |= ENTITY_EVENT_BIT(ENTITY_EVENT_RESET);
  868.         onEventSubscriptions |= ENTITY_EVENT_BIT(ENTITY_EVENT_DONE);
  869.         onEventSubscriptions |= ENTITY_EVENT_BIT(ENTITY_EVENT_HIDE);
  870.         onEventSubscriptions |= ENTITY_EVENT_BIT(ENTITY_EVENT_UNHIDE);
  871.         onEventSubscriptions |= ENTITY_EVENT_BIT(ENTITY_EVENT_XFORM);
  872.  
  873.         gEnv->pEntitySystem->AddSink(this, IEntitySystem::AllSinkEvents & (~IEntitySystem::OnBeforeSpawn), onEventSubscriptions);
  874. }
  875.  
  876. CSmartObjectManager::~CSmartObjectManager()
  877. {
  878.         ClearConditions();
  879.         gEnv->pEntitySystem->RemoveSink(this);
  880.  
  881.         stl::free_container(m_statesToExcludeForPathfinding);
  882. }
  883.  
  884. void CSmartObjectManager::Serialize(TSerialize ser)
  885. {
  886.         if (ser.IsReading())
  887.         {
  888.                 m_vDebugUse.clear();
  889.         }
  890.  
  891.         if (ser.BeginOptionalGroup("SmartObjects", true))
  892.         {
  893.                 if (ser.IsWriting())
  894.                 {
  895.                         int count = g_AllSmartObjects.size();
  896.                         ser.Value("Count", count);
  897.                         SmartObjects::iterator it, itEnd = g_AllSmartObjects.end();
  898.                         for (it = g_AllSmartObjects.begin(); it != itEnd; ++it)
  899.                         {
  900.                                 ser.BeginGroup("Object");
  901.                                 {
  902.                                         CSmartObject* pObject = *it;
  903.                                         SerializePointer(ser, "id", pObject);
  904.                                         if (pObject)
  905.                                                 pObject->Serialize(ser);
  906.                                 }
  907.                                 ser.EndGroup();
  908.                         }
  909.                 }
  910.                 else
  911.                 {
  912.                         int count = 0;
  913.                         ser.Value("Count", count);
  914.                         int foundSmartObjects = 0;
  915.                         for (int i = 0; i < count; i++)
  916.                         {
  917.                                 ser.BeginGroup("Object");
  918.                                 {
  919.                                         CSmartObject* pObject = 0;
  920.                                         SerializePointer(ser, "id", pObject);
  921.                                         if (pObject)
  922.                                         {
  923.                                                 bool bFound = g_AllSmartObjects.find(pObject) != g_AllSmartObjects.end();
  924.                                                 assert(bFound);
  925.                                                 if (bFound) ++foundSmartObjects;
  926.                                                 pObject->Serialize(ser);
  927.                                         }
  928.                                 }
  929.                                 ser.EndGroup();
  930.                         }
  931.  
  932.                         assert(foundSmartObjects == count);
  933.                         assert(foundSmartObjects == g_AllSmartObjects.size());
  934.                 }
  935.  
  936.                 ser.EndGroup(); //SmartObjects
  937.         }
  938.  
  939.         if (ser.IsReading())
  940.         {
  941.                 SoftReset(); // reregister smart objects with navigation
  942.         }
  943. }
  944.  
  945. void CSmartObjectManager::AddSmartObjectClass(CSmartObjectClass* soClass)
  946. {
  947.         CSmartObjectClass::MapSmartObjectsByPos& mapByPos = soClass->m_MapObjectsByPos;
  948.  
  949.         CSmartObjectClasses vClasses;
  950.  
  951.         // we must check already created instances of this class and 'convert' them to smart objects
  952.         IEntitySystem* pEntitySystem = gEnv->pEntitySystem;
  953.         if (pEntitySystem)
  954.         {
  955.                 IEntityItPtr it = pEntitySystem->GetEntityIterator();
  956.                 while (IEntity* pEntity = it->Next())
  957.                 {
  958.                         if (!pEntity->IsGarbage())
  959.                         {
  960.                                 if (ParseClassesFromProperties(pEntity, vClasses))
  961.                                 {
  962.                                         if (std::find(vClasses.begin(), vClasses.end(), soClass) != vClasses.end())
  963.                                         {
  964.                                                 CSmartObject* smartObject = GetSmartObject(pEntity->GetId());
  965.                                                 if (!smartObject)
  966.                                                         smartObject = new CSmartObject(pEntity->GetId());
  967.                                                 soClass->RegisterSmartObject(smartObject);
  968.                                                 smartObject->m_fKey = smartObject->GetPos().x;
  969.                                                 mapByPos.insert(std::make_pair(smartObject->m_fKey, smartObject));
  970.                                         }
  971.                                 }
  972.                         }
  973.                 }
  974.         }
  975. }
  976.  
  977. bool CSmartObjectManager::ParseClassesFromProperties(const IEntity* pEntity, CSmartObjectClasses& vClasses)
  978. {
  979.         vClasses.clear();
  980.  
  981.         IScriptTable* pTable = pEntity->GetScriptTable();
  982.         if (pTable)
  983.         {
  984.                 // ignore AISpawners
  985.                 const char* className = pEntity->GetClass()->GetName();
  986.                 if (strncmp(className, "Spawn", 5) == 0)
  987.                         return false;
  988.  
  989.                 SmartScriptTable props;
  990.                 if (pTable->GetValue("Properties", props))
  991.                 {
  992.                         const char* szClass = NULL;
  993.                         props->GetValue("soclasses_SmartObjectClass", szClass);
  994.  
  995.                         const char* szClassInstance = NULL;
  996.                         SmartScriptTable propsInstance;
  997.                         if (pTable->GetValue("PropertiesInstance", propsInstance))
  998.                                 propsInstance->GetValue("soclasses_SmartObjectClass", szClassInstance);
  999.  
  1000.                         if (szClass && *szClass)
  1001.                                 String2Classes(szClass, vClasses);
  1002.                         if (szClassInstance && *szClassInstance)
  1003.                                 String2Classes(szClassInstance, vClasses);
  1004.                 }
  1005.         }
  1006.  
  1007.         return !vClasses.empty();
  1008. }
  1009.  
  1010. CSmartObjectClass* CSmartObjectManager::GetSmartObjectClass(const char* className)
  1011. {
  1012.         AIAssert(className);
  1013.  
  1014.         if (!*className)
  1015.                 return NULL;
  1016.  
  1017.         CSmartObjectClass* pClass = CSmartObjectClass::find(className);
  1018.         if (pClass)
  1019.                 return pClass;
  1020.  
  1021.         CSmartObjectClass* result = new CSmartObjectClass(className);
  1022.         AddSmartObjectClass(result);
  1023.         return result;
  1024. }
  1025.  
  1026. void CSmartObjectManager::AddSmartObjectCondition(const SmartObjectCondition& conditionData)
  1027. {
  1028.         AIAssert(IsInitialized());
  1029.  
  1030.         CSmartObjectClass* pUserClass = GetSmartObjectClass(conditionData.sUserClass);
  1031.         AIAssert(pUserClass);
  1032.         if (!pUserClass)
  1033.         {
  1034.                 AIWarning("WARNING: Smart Object class '%s' not found while trying to do CSmartObjectManager::AddSmartObjectCondition",
  1035.                           conditionData.sUserClass.c_str());
  1036.                 return;
  1037.         }
  1038.  
  1039.         pUserClass->MarkAsSmartObjectUser();
  1040.  
  1041.         CCondition condition;
  1042.         condition.iTemplateId = conditionData.iTemplateId;
  1043.  
  1044.         condition.pUserClass = pUserClass;
  1045.         String2StatePattern(conditionData.sUserState, condition.userStatePattern);
  1046.  
  1047.         condition.pObjectClass = GetSmartObjectClass(conditionData.sObjectClass);
  1048.         AIAssert(condition.pObjectClass || conditionData.sObjectClass.empty());
  1049.         if (!condition.pObjectClass && !conditionData.sObjectClass.empty())
  1050.         {
  1051.                 AIWarning("WARNING: Smart Object class '%s' not found while trying to do CSmartObjectManager::AddSmartObjectCondition",
  1052.                           conditionData.sObjectClass.c_str());
  1053.                 return;
  1054.         }
  1055.         String2StatePattern(conditionData.sObjectState, condition.objectStatePattern);
  1056.  
  1057.         condition.sUserHelper = conditionData.sUserHelper.c_str();
  1058.         condition.pUserHelper = NULL;
  1059.         condition.sObjectHelper = conditionData.sObjectHelper.c_str();
  1060.         condition.pObjectHelper = NULL;
  1061.  
  1062.         condition.fDistanceFrom = conditionData.fDistanceFrom;
  1063.         condition.fDistanceTo = conditionData.fDistanceTo;
  1064.         condition.fOrientationLimit = conditionData.fOrientationLimit;
  1065.         condition.bHorizLimitOnly = conditionData.bHorizLimitOnly;
  1066.         condition.fOrientationToTargetLimit = conditionData.fOrientationToTargetLimit;
  1067.  
  1068.         condition.fMinDelay = conditionData.fMinDelay;
  1069.         condition.fMaxDelay = conditionData.fMaxDelay;
  1070.         condition.fMemory = conditionData.fMemory;
  1071.  
  1072.         condition.fProximityFactor = conditionData.fProximityFactor;
  1073.         condition.fOrientationFactor = conditionData.fOrientationFactor;
  1074.         condition.fVisibilityFactor = conditionData.fVisibilityFactor;
  1075.         condition.fRandomnessFactor = conditionData.fRandomnessFactor;
  1076.  
  1077.         condition.fLookAtPerc = conditionData.fLookAtOnPerc;
  1078.         String2States(conditionData.sUserPreActionState, condition.userPreActionStates);
  1079.         String2States(conditionData.sObjectPreActionState, condition.objectPreActionStates);
  1080.         condition.eActionType = conditionData.eActionType;
  1081.         condition.sAction = conditionData.sAction;
  1082.         String2States(conditionData.sUserPostActionState, condition.userPostActionStates);
  1083.         String2States(conditionData.sObjectPostActionState, condition.objectPostActionStates);
  1084.  
  1085.         condition.iMaxAlertness = conditionData.iMaxAlertness;
  1086.         condition.bEnabled = conditionData.bEnabled;
  1087.         condition.sName = conditionData.sName;
  1088.         condition.sDescription = conditionData.sDescription;
  1089.         condition.sFolder = conditionData.sFolder;
  1090.         condition.iOrder = conditionData.iOrder;
  1091.         condition.sEvent = conditionData.sEvent;
  1092.         condition.sChainedUserEvent = conditionData.sChainedUserEvent;
  1093.         condition.sChainedObjectEvent = conditionData.sChainedObjectEvent;
  1094.  
  1095.         condition.iRuleType = conditionData.iRuleType;
  1096.         condition.sEntranceHelper = conditionData.sEntranceHelper;
  1097.         condition.sExitHelper = conditionData.sExitHelper;
  1098.  
  1099.         condition.fApproachSpeed = conditionData.fApproachSpeed;
  1100.         condition.iApproachStance = conditionData.iApproachStance;
  1101.         condition.sAnimationHelper = conditionData.sAnimationHelper;
  1102.         condition.sApproachHelper = conditionData.sApproachHelper;
  1103.         condition.fStartWidth = conditionData.fStartWidth;
  1104.         condition.fDirectionTolerance = conditionData.fDirectionTolerance;
  1105.         condition.fStartArcAngle = conditionData.fStartArcAngle;
  1106.  
  1107.         /*
  1108.            MapConditions::iterator it, itEnd = m_Conditions.end();
  1109.            for ( it = m_Conditions.begin(); it != itEnd; ++it )
  1110.             if ( condition.iOrder <= it->second.iOrder )
  1111.            ++it->second.iOrder;
  1112.          */
  1113.  
  1114.         MapConditions::iterator where = m_Conditions.insert(std::make_pair(pUserClass, condition));
  1115.         CCondition* pCondition = &where->second;
  1116.  
  1117.         if (!condition.sEvent.empty())
  1118.         {
  1119.                 CEvent* pEvent = String2Event(condition.sEvent);
  1120.                 pEvent->m_Conditions.insert(std::make_pair(pUserClass, pCondition));
  1121.         }
  1122.         else if (pCondition->bEnabled && pCondition->iRuleType == 0)
  1123.         {
  1124.                 // optimization: each user class should know what rules to use during update
  1125.                 for (int i = 0; i <= CLAMP(condition.iMaxAlertness, 0, 2); ++i)
  1126.                         pUserClass->m_vActiveUpdateRules[i].push_back(pCondition);
  1127.         }
  1128. }
  1129.  
  1130. void CSmartObjectManager::ClearConditions()
  1131. {
  1132.         Reset();
  1133.         stl::free_container(m_Conditions);
  1134.  
  1135.         // only remove pointers to conditions
  1136.         // keep the events to preserve their descriptions
  1137.         MapEvents::iterator it = m_mapEvents.begin(), itEnd = m_mapEvents.end();
  1138.         MapEvents::iterator next;
  1139.         while (it != itEnd)
  1140.         {
  1141.                 next = it;
  1142.                 ++next;
  1143.                 if (it->second.m_Conditions.size())
  1144.                 {
  1145.                         stl::free_container(it->second.m_Conditions);
  1146.                 }
  1147.                 else
  1148.                 {
  1149.                         // an event not associated with any rule - delete the event
  1150.                         m_mapEvents.erase(it);
  1151.                 }
  1152.                 it = next;
  1153.         }
  1154.  
  1155.         // clean the SOClasses pointing to active rules
  1156.         CSmartObjectClass::MapClassesByName::iterator itClasses = CSmartObjectClass::g_AllByName.begin();
  1157.         while (itClasses != CSmartObjectClass::g_AllByName.end())
  1158.         {
  1159.                 itClasses->second->m_vActiveUpdateRules[0].clear();
  1160.                 itClasses->second->m_vActiveUpdateRules[1].clear();
  1161.                 itClasses->second->m_vActiveUpdateRules[2].clear();
  1162.                 ++itClasses;
  1163.         }
  1164. }
  1165.  
  1166. void CSmartObjectManager::Reset()
  1167. {
  1168.         CClassTemplateData::m_pHelperMtl = NULL;
  1169.  
  1170.         CSmartObjectClass::MapClassesByName::iterator itClasses = CSmartObjectClass::g_AllByName.begin();
  1171.         while (itClasses != CSmartObjectClass::g_AllByName.end())
  1172.         {
  1173.                 delete (itClasses++)->second;
  1174.         }
  1175.         stl::free_container(CSmartObjectClass::g_AllByName);
  1176.         stl::free_container(CSmartObjectClass::g_AllUserClasses);
  1177.         CSmartObjectClass::g_itAllUserClasses = CSmartObjectClass::g_AllUserClasses.end();
  1178.         stl::free_container(m_vDebugUse);
  1179.         stl::free_container(m_tmpVecDelayTimes);
  1180.         CSmartObject::CState::Reset();
  1181. }
  1182.  
  1183. void CSmartObjectManager::SoftReset()
  1184. {
  1185.         CClassTemplateData::m_pHelperMtl = NULL;
  1186.  
  1187.         CSmartObjectClass::MapClassesByName::iterator itClasses = CSmartObjectClass::g_AllByName.begin();
  1188.         while (itClasses != CSmartObjectClass::g_AllByName.end())
  1189.         {
  1190.                 itClasses->second->m_MapObjectsByPos.clear();
  1191.                 ++itClasses;
  1192.         }
  1193.         m_vDebugUse.clear();
  1194.         //m_SmartObjects.clear();
  1195.  
  1196.         // re-register entities with the smart objects system
  1197.         IEntityIt* it = gEnv->pEntitySystem->GetEntityIterator();
  1198.         while (!it->IsEnd())
  1199.         {
  1200.                 IEntity* pEntity = it->Next();
  1201.                 DoRemove(pEntity);
  1202.  
  1203.                 SEntitySpawnParams params;
  1204.                 OnSpawn(pEntity, params);
  1205.         }
  1206.         it->Release();
  1207.  
  1208.         RebuildNavigation();
  1209.         m_bRecalculateUserSize = true;
  1210. }
  1211.  
  1212. void CSmartObjectManager::RecalculateUserSize()
  1213. {
  1214.         m_bRecalculateUserSize = false;
  1215.  
  1216.         //Do this first before adding up the max size on the object classes, so that the user class sizes are valid.
  1217.         SmartObjects::iterator it, itEnd = g_AllSmartObjects.end();
  1218.         for (it = g_AllSmartObjects.begin(); it != itEnd; ++it)
  1219.         {
  1220.                 CSmartObject* pObject = *it;
  1221.                 if (pObject)
  1222.                         pObject->ApplyUserSize();
  1223.         }
  1224.  
  1225.         // find navigation rules and add the size of the user to the used object class
  1226.         MapConditions::iterator itConditions, itConditionsEnd = m_Conditions.end();
  1227.         for (itConditions = m_Conditions.begin(); itConditions != itConditionsEnd; ++itConditions)
  1228.         {
  1229.                 // ignore event rules
  1230.                 if (!itConditions->second.sEvent.empty())
  1231.                         continue;
  1232.  
  1233.                 CSmartObjectClass* pClass = itConditions->first;
  1234.                 CCondition* pCondition = &itConditions->second;
  1235.                 if (pCondition->bEnabled && pCondition->pObjectClass && pCondition->iRuleType == 1)
  1236.                 {
  1237.                         pCondition->pObjectClass->AddHelperLink(pCondition);
  1238.                         pCondition->pObjectClass->AddNavUserClass(pCondition->pUserClass);
  1239.                 }
  1240.         }
  1241. }
  1242.  
  1243. float CSmartObjectManager::CalculateDelayTime(CSmartObject* pUser, const Vec3& posUser,
  1244.                                               CSmartObject* pObject, const Vec3& posObject, CCondition* pCondition) const
  1245. {
  1246.         FUNCTION_PROFILER(GetISystem(), PROFILE_AI);
  1247.  
  1248.         if (!pCondition->bEnabled)
  1249.                 return -1.0f;
  1250.  
  1251.         if (pUser == pObject)
  1252.         {
  1253.                 // this is a relation on itself
  1254.                 // just ignore all calculations (except randomness)
  1255.  
  1256.                 float delta = 0.5f;
  1257.  
  1258.                 // calculate randomness
  1259.                 if (pCondition->fRandomnessFactor)
  1260.                 {
  1261.                         delta *= 1.0f + (pUser->m_fRandom + pObject->m_fRandom - 0.5f) * pCondition->fRandomnessFactor;
  1262.                 }
  1263.  
  1264.                 return pCondition->fMinDelay + (pCondition->fMaxDelay - pCondition->fMinDelay) * (1.0f - delta);
  1265.         }
  1266.  
  1267.         Vec3 direction = posObject - posUser;
  1268.  
  1269.         float limitFrom2 = pCondition->fDistanceFrom;
  1270.         float limitTo2 = pCondition->fDistanceTo;
  1271.  
  1272.         //      if ( direction.x > limit2 || direction.x < -limit2 ||
  1273.         //               direction.y > limit2 || direction.y < -limit2 ||
  1274.         //               direction.z > limit2 || direction.z < -limit2 )
  1275.         //              return -1.0f;
  1276.  
  1277.         limitFrom2 *= limitFrom2;
  1278.         limitTo2 *= limitTo2;
  1279.         float distance2 = direction.GetLengthSquared();
  1280.  
  1281.         if (pCondition->fDistanceTo && (distance2 > limitTo2 || distance2 < limitFrom2))
  1282.                 return -1.0f;
  1283.  
  1284.         float dot = 2.0f;
  1285.         if (pCondition->fOrientationLimit < 360.0f)
  1286.         {
  1287.                 float cosLimit = 1.0f;
  1288.                 if (pCondition->fOrientationLimit != 0)
  1289.                         cosLimit = cosf(pCondition->fOrientationLimit / 360.0f * 3.1415926536f);   // limit is expressed as FOV (360 means unlimited)
  1290.  
  1291.                 dot = CalculateDot(pCondition, direction, pUser);
  1292.  
  1293.                 if (pCondition->fOrientationLimit < 0)
  1294.                 {
  1295.                         dot = -dot;
  1296.                         cosLimit = -cosLimit;
  1297.                 }
  1298.  
  1299.                 if (dot < cosLimit)
  1300.                         return -1.0f;
  1301.         }
  1302.  
  1303.         float delta = 0.0f;
  1304.         float offset = 0.0f;
  1305.         float divider = 0.0f;
  1306.  
  1307.         // calculate distance
  1308.         if (pCondition->fProximityFactor)
  1309.         {
  1310.                 delta += (1.0f - sqrtf(distance2 / limitTo2)) * pCondition->fProximityFactor;
  1311.                 if (pCondition->fProximityFactor > 0)
  1312.                         divider += pCondition->fProximityFactor;
  1313.                 else
  1314.                 {
  1315.                         offset -= pCondition->fProximityFactor;
  1316.                         divider -= pCondition->fProximityFactor;
  1317.                 }
  1318.         }
  1319.  
  1320.         // calculate orientation
  1321.         if (pCondition->fOrientationFactor)
  1322.         {
  1323.                 // TODO: Could be optimized
  1324.                 float cosLimit = -1.0f;
  1325.                 if (pCondition->fOrientationLimit != 0 && pCondition->fOrientationLimit < 360.0f && pCondition->fOrientationLimit >= -360.0f)
  1326.                         cosLimit = cosf(pCondition->fOrientationLimit / 360.0f * 3.1415926536f);   // limit is expressed as FOV (360 means unlimited)
  1327.                 if (pCondition->fOrientationLimit < 0)
  1328.                         cosLimit = -cosLimit;
  1329.  
  1330.                 if (dot == 2.0f)
  1331.                 {
  1332.                         dot = CalculateDot(pCondition, direction, pUser);
  1333.                         if (pCondition->fOrientationLimit < 0)
  1334.                                 dot = -dot;
  1335.                 }
  1336.  
  1337.                 float factor;
  1338.                 if (abs(pCondition->fOrientationLimit) <= 0.01f)
  1339.                         factor = dot >= 0.99f ? 1.0f : 0.0f;
  1340.                 else
  1341.                         factor = (dot - cosLimit) / (1.0f - cosLimit);
  1342.  
  1343.                 delta += factor * pCondition->fOrientationFactor;
  1344.                 if (pCondition->fOrientationFactor > 0)
  1345.                         divider += pCondition->fOrientationFactor;
  1346.                 else
  1347.                 {
  1348.                         offset -= pCondition->fOrientationFactor;
  1349.                         divider -= pCondition->fOrientationFactor;
  1350.                 }
  1351.         }
  1352.  
  1353.         // calculate visibility
  1354.         if (pCondition->fVisibilityFactor)
  1355.         {
  1356.                 CPipeUser* pPipeUser = pUser->GetPipeUser();
  1357.                 const bool isInVisualRange = pPipeUser ? (IAIObject::eFOV_Outside != pPipeUser->IsPointInFOV(posObject)) : false;
  1358.                 if (isInVisualRange)
  1359.                         if (GetAISystem()->CheckPointsVisibility(posUser, posObject, 0, pUser->GetPhysics(), pObject->GetPhysics()))   // is physically visible?
  1360.                                 delta += pCondition->fVisibilityFactor;
  1361.                 if (pCondition->fVisibilityFactor > 0)
  1362.                         divider += pCondition->fVisibilityFactor;
  1363.                 else
  1364.                 {
  1365.                         offset -= pCondition->fVisibilityFactor;
  1366.                         divider -= pCondition->fVisibilityFactor;
  1367.                 }
  1368.         }
  1369.  
  1370.         if (!pCondition->fProximityFactor && !pCondition->fOrientationFactor && !pCondition->fVisibilityFactor)
  1371.                 delta = 0.5f;
  1372.  
  1373.         // calculate randomness
  1374.         if (pCondition->fRandomnessFactor)
  1375.         {
  1376.                 delta *= 1.0f + (pUser->m_fRandom + pObject->m_fRandom - 0.5f) * pCondition->fRandomnessFactor;
  1377.         }
  1378.  
  1379.         if (divider)
  1380.                 delta = (delta + offset) / divider;
  1381.  
  1382.         return pCondition->fMinDelay + (pCondition->fMaxDelay - pCondition->fMinDelay) * (1.0f - delta);
  1383. }
  1384.  
  1385. int CSmartObjectManager::TriggerEvent(const char* sEventName, IEntity*& pUser, IEntity*& pObject, QueryEventMap* pQueryEvents /*= NULL*/, const Vec3* pExtraPoint /*= NULL*/, bool bHighPriority /*= false*/)
  1386. {
  1387.         if (!IsInitialized())
  1388.                 return 0;
  1389.  
  1390.         if (!sEventName || !*sEventName)
  1391.                 return 0;
  1392.  
  1393.         CSmartObject* pSOUser = NULL;
  1394.         if (pUser)
  1395.         {
  1396.                 if (pUser->IsHidden())
  1397.                 {
  1398.                         // ignore hidden entities
  1399.                         return 0;
  1400.                 }
  1401.                 pSOUser = GetSmartObject(pUser->GetId());
  1402.                 if (!pSOUser)
  1403.                         return 0; // requested user is not a smart object
  1404.         }
  1405.         CSmartObject* pSOObject = NULL;
  1406.         if (pObject)
  1407.         {
  1408.                 if (pObject->IsHidden())
  1409.                 {
  1410.                         // ignore hidden entities
  1411.                         return 0;
  1412.                 }
  1413.                 pSOObject = GetSmartObject(pObject->GetId());
  1414.                 if (!pSOObject)
  1415.                         return 0; // requested object is not a smart object
  1416.         }
  1417.  
  1418.         if (pSOUser && pSOObject)
  1419.                 return TriggerEventUserObject(sEventName, pSOUser, pSOObject, pQueryEvents, pExtraPoint, bHighPriority);
  1420.         else if (pSOUser)
  1421.                 return TriggerEventUser(sEventName, pSOUser, pQueryEvents, &pObject, pExtraPoint, bHighPriority);
  1422.         else if (pSOObject)
  1423.                 return TriggerEventObject(sEventName, pSOObject, pQueryEvents, &pUser, pExtraPoint, bHighPriority);
  1424.  
  1425.         // specific user or object is not requested
  1426.         // check the rules on all users and objects
  1427.  
  1428.         float minDelay = FLT_MAX;
  1429.         CCondition* pMinRule = NULL;
  1430.         CSmartObject* pMinUser = NULL;
  1431.         CSmartObject* pMinObject = NULL;
  1432.         CEvent* pEvent = String2Event(sEventName);
  1433.  
  1434.         MapPtrConditions::iterator itRules, itRulesEnd = pEvent->m_Conditions.end();
  1435.         for (itRules = pEvent->m_Conditions.begin(); itRules != itRulesEnd; ++itRules)
  1436.         {
  1437.                 CCondition* pRule = itRules->second;
  1438.                 if (!pRule->bEnabled)
  1439.                         continue;
  1440.  
  1441.                 // if we already have found a rule with a delay less than min. delay of this rule then ignore this rule
  1442.                 if (!pQueryEvents && minDelay < pRule->fMinDelay)
  1443.                         continue;
  1444.  
  1445.                 CSmartObjectClass::MapSmartObjectsByPos& mapByPos = pRule->pUserClass->m_MapObjectsByPos;
  1446.                 if (mapByPos.empty())
  1447.                         continue;
  1448.  
  1449.                 CSmartObjectClass::MapSmartObjectsByPos::iterator itByPos, itByPosEnd = mapByPos.end();
  1450.                 for (itByPos = mapByPos.begin(); itByPos != itByPosEnd; ++itByPos)
  1451.                 {
  1452.                         pSOUser = itByPos->second;
  1453.  
  1454.                         // ignore hidden entities
  1455.                         if (pSOUser->IsHidden())
  1456.                                 continue;
  1457.  
  1458.                         // proceed with next user if this one doesn't match states
  1459.                         if (!pRule->userStatePattern.Matches(pSOUser->GetStates()))
  1460.                                 continue;
  1461.  
  1462.                         // now for this user check all objects matching the rule's conditions
  1463.  
  1464.                         Vec3 soPos = pSOUser->GetPos();
  1465.                         CAIActor* pAIActor = pSOUser->GetAIActor();
  1466.                         IAIObject* pAttTarget = pAIActor ? pAIActor->GetAttentionTarget() : NULL;
  1467.  
  1468.                         // ignore this user if it has no attention target and the rule says it's needed
  1469.                         if (!pExtraPoint && !pAttTarget && pRule->fOrientationToTargetLimit < 360.0f)
  1470.                                 continue;
  1471.  
  1472.                         int alertness = pAIActor ? (pAIActor->IsEnabled() ? pAIActor->GetProxy()->GetAlertnessState() : 1000) : 0;
  1473.                         if (alertness > pRule->iMaxAlertness)
  1474.                                 continue; // ignore this user - too much alerted
  1475.  
  1476.                         // first check does it maybe operate only on itself
  1477.                         if (!pRule->pObjectClass)
  1478.                         {
  1479.                                 // calculate delay time
  1480.                                 float fDelayTime = CalculateDelayTime(pSOUser, soPos, pSOUser, soPos, pRule);
  1481.                                 if (fDelayTime >= 0.0f && fDelayTime <= minDelay)
  1482.                                 {
  1483.                                         // mark this as best
  1484.                                         minDelay = fDelayTime;
  1485.                                         pMinUser = pMinObject = pSOUser;
  1486.                                         pMinRule = pRule;
  1487.                                 }
  1488.  
  1489.                                 // add it to the query list
  1490.                                 if (pQueryEvents && fDelayTime >= 0.0f)
  1491.                                 {
  1492.                                         CQueryEvent q;
  1493.                                         q.pUser = pSOUser;
  1494.                                         q.pObject = pSOUser;
  1495.                                         q.pRule = pRule;
  1496.                                         q.pChainedUserEvent = NULL;
  1497.                                         q.pChainedObjectEvent = NULL;
  1498.                                         pQueryEvents->insert(std::make_pair(fDelayTime, q));
  1499.                                 }
  1500.  
  1501.                                 // proceed with next user
  1502.                                 continue;
  1503.                         }
  1504.  
  1505.                         // get species and group id of the user
  1506.                         uint8 factionID = IFactionMap::InvalidFactionID;
  1507.                         int groupId = -1;
  1508.  
  1509.                         CAIActor* pUserActor = pSOUser->GetAIActor();
  1510.                         if (pUserActor)
  1511.                         {
  1512.                                 groupId = pUserActor->GetGroupId();
  1513.                                 factionID = pUserActor->GetParameters().factionID;
  1514.                         }
  1515.  
  1516.                         // adjust pos if pUserHelper is used
  1517.                         Vec3 pos = pRule->pUserHelper ? pSOUser->GetHelperPos(pRule->pUserHelper) : soPos;
  1518.  
  1519.                         Vec3 bbMin = pos;
  1520.                         Vec3 bbMax = pos;
  1521.  
  1522.                         float limitTo = pRule->fDistanceTo;
  1523.                         // adjust limit if pUserHelper is used
  1524.                         if (pRule->pUserHelper)
  1525.                                 limitTo += pRule->pUserHelper->qt.t.GetLength();
  1526.                         // adjust limit if pObjectHelper is used
  1527.                         if (pRule->pObjectHelper)
  1528.                                 limitTo += pRule->pObjectHelper->qt.t.GetLength();
  1529.                         Vec3 d(limitTo, limitTo, limitTo);
  1530.                         bbMin -= d;
  1531.                         bbMax += d;
  1532.  
  1533.                         // calculate the limit in advance
  1534.                         float orientationToTargetLimit = -2.0f; // unlimited
  1535.                         if (pRule->fOrientationToTargetLimit < 360.0f)
  1536.                         {
  1537.                                 if (pRule->fOrientationToTargetLimit == 0)
  1538.                                         orientationToTargetLimit = 1.0f;
  1539.                                 else
  1540.                                         orientationToTargetLimit = cosf(pRule->fOrientationToTargetLimit / 360.0f * 3.1415926536f);   // limit is expressed as FOV (360 means unlimited)
  1541.  
  1542.                                 if (pRule->fOrientationToTargetLimit < 0)
  1543.                                         orientationToTargetLimit = -orientationToTargetLimit;
  1544.                         }
  1545.  
  1546.                         // check all objects (but not the user) matching with condition's class and state
  1547.                         CSmartObjectClass::MapSmartObjectsByPos& mapObjectByPos = pRule->pObjectClass->m_MapObjectsByPos;
  1548.                         if (mapObjectByPos.empty())
  1549.                                 continue;
  1550.  
  1551.                         CSmartObjectClass::MapSmartObjectsByPos::iterator itObjectByPos, itObjectByPosEnd = mapObjectByPos.upper_bound(bbMax.x);
  1552.                         for (itObjectByPos = mapObjectByPos.lower_bound(bbMin.x); itObjectByPos != itObjectByPosEnd; ++itObjectByPos)
  1553.                         {
  1554.                                 pSOObject = itObjectByPos->second;
  1555.  
  1556.                                 // the user must be different than target object!!!
  1557.                                 if (pSOUser == pSOObject)
  1558.                                         continue;
  1559.  
  1560.                                 // ignore hidden entities
  1561.                                 if (pSOObject->IsHidden())
  1562.                                         continue;
  1563.  
  1564.                                 // Check range first since it could be faster
  1565.                                 Vec3 objectPos = pRule->pObjectHelper ? pSOObject->GetHelperPos(pRule->pObjectHelper) : pSOObject->GetPos();
  1566.                                 assert(bbMin.x - limitTo <= objectPos.x && objectPos.x <= bbMax.x + limitTo);
  1567.                                 if (objectPos.y < bbMin.y || objectPos.y > bbMax.y ||
  1568.                                     objectPos.z < bbMin.z || objectPos.z > bbMax.z)
  1569.                                         continue;
  1570.  
  1571.                                 // Also check the orientation limit to user's attention target
  1572.                                 if (pRule->fOrientationToTargetLimit < 360.0f)
  1573.                                 {
  1574.                                         Vec3 objectDir = pSOObject->GetOrientation(pRule->pObjectHelper);
  1575.                                         Vec3 targetDir = (pExtraPoint ? *pExtraPoint : pAttTarget->GetPos()) - objectPos;
  1576.                                         targetDir.NormalizeSafe();
  1577.                                         float dot = objectDir.Dot(targetDir);
  1578.  
  1579.                                         if (pRule->fOrientationToTargetLimit < 0)
  1580.                                                 dot = -dot;
  1581.  
  1582.                                         if (dot < orientationToTargetLimit)
  1583.                                                 continue;
  1584.                                 }
  1585.  
  1586.                                 // add virtual states
  1587.                                 //                              IAIObject* pAIObjectObject = pSOObject->GetAI();
  1588.                                 CAIActor* pAIObjectObject = pSOObject->GetAIActor();
  1589.                                 bool attTarget = false;
  1590.                                 bool sameGroupId = false;
  1591.                                 bool sameFaction = false;
  1592.                                 if (pAIObjectObject)
  1593.                                 {
  1594.                                         // check is the object attention target of the user
  1595.                                         attTarget = pAIActor && pAIActor->GetAttentionTarget() == pSOObject->GetAI();   //pAIObjectObject;
  1596.                                         if (attTarget)
  1597.                                                 pSOObject->m_States.insert(m_StateAttTarget);
  1598.  
  1599.                                         // check are the user and the object in the same group and species
  1600.                                         if (groupId >= 0 || factionID != IFactionMap::InvalidFactionID)
  1601.                                         {
  1602.                                                 // check same species
  1603.                                                 uint8 objectFactionID = pAIObjectObject->GetFactionID();
  1604.                                                 sameFaction = factionID == objectFactionID;
  1605.                                                 if (sameFaction)
  1606.                                                 {
  1607.                                                         pSOObject->m_States.insert(m_StateSameFaction);
  1608.  
  1609.                                                         // if they are same species check are they in same group
  1610.                                                         sameGroupId = groupId == pAIObjectObject->GetGroupId();
  1611.                                                         if (sameGroupId)
  1612.                                                                 pSOObject->m_States.insert(m_StateSameGroup);
  1613.                                                 }
  1614.                                                 else if ((factionID == IFactionMap::InvalidFactionID) || (objectFactionID == IFactionMap::InvalidFactionID))
  1615.                                                 {
  1616.                                                         // if any of them has no species check are they in same group
  1617.                                                         sameGroupId = groupId == pAIObjectObject->GetGroupId();
  1618.                                                         if (sameGroupId)
  1619.                                                                 pSOObject->m_States.insert(m_StateSameGroup);
  1620.                                                 }
  1621.                                         }
  1622.                                 }
  1623.  
  1624.                                 // check object's state pattern and then remove virtual states
  1625.                                 bool bMatches = pRule->objectStatePattern.Matches(pSOObject->GetStates());
  1626.                                 if (attTarget)
  1627.                                         pSOObject->m_States.erase(m_StateAttTarget);
  1628.                                 if (sameFaction)
  1629.                                         pSOObject->m_States.erase(m_StateSameFaction);
  1630.                                 if (sameGroupId)
  1631.                                         pSOObject->m_States.erase(m_StateSameGroup);
  1632.  
  1633.                                 // ignore this object if it doesn't match precondition state
  1634.                                 if (!bMatches)
  1635.                                         continue;
  1636.  
  1637.                                 // calculate delay time
  1638.                                 float fDelayTime = CalculateDelayTime(pSOUser, pos, pSOObject, objectPos, pRule);
  1639.                                 if (fDelayTime >= 0.0f && fDelayTime <= minDelay)
  1640.                                 {
  1641.                                         minDelay = fDelayTime;
  1642.                                         pMinUser = pSOUser;
  1643.                                         pMinObject = pSOObject;
  1644.                                         pMinRule = pRule;
  1645.                                 }
  1646.  
  1647.                                 // add it to the query list
  1648.                                 if (pQueryEvents && fDelayTime >= 0.0f)
  1649.                                 {
  1650.                                         CQueryEvent q;
  1651.                                         q.pUser = pSOUser;
  1652.                                         q.pObject = pSOObject;
  1653.                                         q.pRule = pRule;
  1654.                                         q.pChainedUserEvent = NULL;
  1655.                                         q.pChainedObjectEvent = NULL;
  1656.                                         pQueryEvents->insert(std::make_pair(fDelayTime, q));
  1657.                                 }
  1658.                         }
  1659.                 }
  1660.         }
  1661.  
  1662.         if (!pMinRule)
  1663.                 return 0;
  1664.  
  1665.         // is querying only?
  1666.         if (pQueryEvents)
  1667.                 return -1;
  1668.  
  1669.         int id = GetAISystem()->AllocGoalPipeId();
  1670.         UseSmartObject(pMinUser, pMinObject, pMinRule, id, bHighPriority);
  1671.         pUser = pMinUser->GetEntity();
  1672.         pObject = pMinObject ? pMinObject->GetEntity() : NULL;
  1673.         return !pMinRule->sAction.empty() && pMinRule->eActionType != eAT_None ? id : -1;
  1674. }
  1675.  
  1676. int CSmartObjectManager::TriggerEventUserObject(const char* sEventName, CSmartObject* pUser, CSmartObject* pObject, QueryEventMap* pQueryEvents, const Vec3* pExtraPoint, bool bHighPriority /*=false*/)
  1677. {
  1678.         float minDelay = FLT_MAX;
  1679.         CCondition* pMinRule = NULL;
  1680.         CEvent* pEvent = String2Event(sEventName);
  1681.  
  1682.         CAIActor* pAIActor = pUser->GetAIActor();
  1683.         IAIObject* pAttTarget = pAIActor ? pAIActor->GetAttentionTarget() : NULL;
  1684.         int alertness = pAIActor ? (pAIActor->IsEnabled() ? pAIActor->GetProxy()->GetAlertnessState() : 1000) : 0;
  1685.  
  1686.         CSmartObjectClasses::iterator itUserClasses, itUserClassesEnd = pUser->GetClasses().end();
  1687.         for (itUserClasses = pUser->GetClasses().begin(); itUserClasses != itUserClassesEnd; ++itUserClasses)
  1688.         {
  1689.                 CSmartObjectClass* pUserClass = *itUserClasses;
  1690.                 MapPtrConditions::iterator itRules, itRulesEnd = pEvent->m_Conditions.upper_bound(pUserClass);
  1691.                 for (itRules = pEvent->m_Conditions.lower_bound(pUserClass); itRules != itRulesEnd; ++itRules)
  1692.                 {
  1693.                         CCondition* pRule = itRules->second;
  1694.                         if (!pRule->bEnabled)
  1695.                                 continue;
  1696.  
  1697.                         // if we already have found a rule with a delay less than min. delay of this rule then ignore this rule
  1698.                         if (!pQueryEvents && minDelay < pRule->fMinDelay)
  1699.                                 continue;
  1700.  
  1701.                         // check interaction and rule types
  1702.                         if (pUser != pObject && !pRule->pObjectClass)
  1703.                                 continue;
  1704.                         if (pUser == pObject && pRule->pObjectClass)
  1705.                                 continue;
  1706.  
  1707.                         // proceed with the next rule if user doesn't match states with this one
  1708.                         if (!pRule->userStatePattern.Matches(pUser->GetStates()))
  1709.                                 continue;
  1710.  
  1711.                         if (alertness > pRule->iMaxAlertness)
  1712.                                 continue; // ignore this rule - too much alerted
  1713.  
  1714.                         // ignore this user if it has no attention target and the rule says it's needed
  1715.                         if (!pExtraPoint && !pAttTarget && pRule->fOrientationToTargetLimit < 360.0f)
  1716.                                 continue;
  1717.  
  1718.                         // adjust pos if helpers are used
  1719.                         Vec3 pos = pRule->pUserHelper ? pUser->GetHelperPos(pRule->pUserHelper) : pUser->GetPos();
  1720.  
  1721.                         // now for this user check the rule with requested object
  1722.  
  1723.                         if (pUser == pObject)
  1724.                         {
  1725.                                 // calculate delay time
  1726.                                 float fDelayTime = CalculateDelayTime(pUser, pos, pUser, pos, pRule);
  1727.                                 if (fDelayTime >= 0.0f && fDelayTime <= minDelay)
  1728.                                 {
  1729.                                         minDelay = fDelayTime;
  1730.                                         pMinRule = pRule;
  1731.                                 }
  1732.  
  1733.                                 // add it to the query list
  1734.                                 if (pQueryEvents && fDelayTime >= 0.0f)
  1735.                                 {
  1736.                                         CQueryEvent q;
  1737.                                         q.pUser = pUser;
  1738.                                         q.pObject = pUser;
  1739.                                         q.pRule = pRule;
  1740.                                         q.pChainedUserEvent = NULL;
  1741.                                         q.pChainedObjectEvent = NULL;
  1742.                                         pQueryEvents->insert(std::make_pair(fDelayTime, q));
  1743.                                 }
  1744.  
  1745.                                 continue;
  1746.                         }
  1747.  
  1748.                         // ignore rules which object's class is different
  1749.                         CSmartObjectClasses& objectClasses = pObject->GetClasses();
  1750.                         if (std::find(objectClasses.begin(), objectClasses.end(), pRule->pObjectClass) == objectClasses.end())
  1751.                                 continue;
  1752.  
  1753.                         // adjust pos if helpers are used
  1754.                         Vec3 objectPos = pRule->pObjectHelper ? pObject->GetHelperPos(pRule->pObjectHelper) : pObject->GetPos();
  1755.  
  1756.                         // check the orientation limit to target
  1757.                         if (pRule->fOrientationToTargetLimit < 360.0f)
  1758.                         {
  1759.                                 float cosLimit = 1.0f;
  1760.                                 if (pRule->fOrientationToTargetLimit != 0)
  1761.                                         cosLimit = cosf(pRule->fOrientationToTargetLimit / 360.0f * 3.1415926536f);   // limit is expressed as FOV (360 means unlimited)
  1762.  
  1763.                                 Vec3 objectDir = pObject->GetOrientation(pRule->pObjectHelper);
  1764.                                 Vec3 targetDir = (pExtraPoint ? *pExtraPoint : pAttTarget->GetPos()) - objectPos;
  1765.                                 targetDir.NormalizeSafe();
  1766.                                 float dot = objectDir.Dot(targetDir);
  1767.  
  1768.                                 if (pRule->fOrientationToTargetLimit < 0)
  1769.                                 {
  1770.                                         dot = -dot;
  1771.                                         cosLimit = -cosLimit;
  1772.                                 }
  1773.  
  1774.                                 if (dot < cosLimit)
  1775.                                         continue;
  1776.                         }
  1777.  
  1778.                         // get species and group id of the user
  1779.                         uint8 factionID = IFactionMap::InvalidFactionID;
  1780.                         int groupId = -1;
  1781.                         CAIActor* pUserActor = pUser->GetAIActor();
  1782.                         if (pUserActor)
  1783.                         {
  1784.                                 groupId = pUserActor->GetGroupId();
  1785.                                 factionID = pUserActor->GetFactionID();
  1786.                         }
  1787.  
  1788.                         // add virtual states
  1789.                         CAIActor* pAIObjectObject = pObject->GetAIActor();
  1790.                         bool attTarget = false;
  1791.                         bool sameGroupId = false;
  1792.                         bool sameFaction = false;
  1793.                         if (pAIObjectObject)
  1794.                         {
  1795.                                 // check is the object attention target of the user
  1796.                                 attTarget = pAIActor && pAIActor->GetAttentionTarget() == pObject->GetAI();
  1797.                                 if (attTarget)
  1798.                                         pObject->m_States.insert(m_StateAttTarget);
  1799.  
  1800.                                 // check are the user and the object in the same group and species
  1801.                                 if (groupId >= 0 || (factionID != IFactionMap::InvalidFactionID))
  1802.                                 {
  1803.                                         // check same species
  1804.                                         uint8 objectFactionID = pAIObjectObject->GetFactionID();
  1805.                                         sameFaction = factionID == objectFactionID;
  1806.                                         if (sameFaction)
  1807.                                         {
  1808.                                                 pObject->m_States.insert(m_StateSameFaction);
  1809.  
  1810.                                                 // if they are same species check are they in same group
  1811.                                                 sameGroupId = groupId == pAIObjectObject->GetGroupId();
  1812.                                                 if (sameGroupId)
  1813.                                                         pObject->m_States.insert(m_StateSameGroup);
  1814.                                         }
  1815.                                         else if ((factionID == IFactionMap::InvalidFactionID) || (objectFactionID == IFactionMap::InvalidFactionID))
  1816.                                         {
  1817.                                                 // if any of them has no species check are they in same group
  1818.                                                 sameGroupId = groupId == pAIObjectObject->GetGroupId();
  1819.                                                 if (sameGroupId)
  1820.                                                         pObject->m_States.insert(m_StateSameGroup);
  1821.                                         }
  1822.                                 }
  1823.                         }
  1824.  
  1825.                         // check object's state pattern and then remove virtual states
  1826.                         bool bMatches = pRule->objectStatePattern.Matches(pObject->GetStates());
  1827.                         if (attTarget)
  1828.                                 pObject->m_States.erase(m_StateAttTarget);
  1829.                         if (sameFaction)
  1830.                                 pObject->m_States.erase(m_StateSameFaction);
  1831.                         if (sameGroupId)
  1832.                                 pObject->m_States.erase(m_StateSameGroup);
  1833.  
  1834.                         // ignore this object if it doesn't match precondition state
  1835.                         if (!bMatches)
  1836.                                 continue;
  1837.  
  1838.                         // calculate delay time
  1839.                         float fDelayTime = CalculateDelayTime(pUser, pos, pObject, objectPos, pRule);
  1840.                         if (fDelayTime >= 0.0f && fDelayTime <= minDelay)
  1841.                         {
  1842.                                 minDelay = fDelayTime;
  1843.                                 pMinRule = pRule;
  1844.                         }
  1845.  
  1846.                         // add it to the query list
  1847.                         if (pQueryEvents && fDelayTime >= 0.0f)
  1848.                         {
  1849.                                 CQueryEvent q;
  1850.                                 q.pUser = pUser;
  1851.                                 q.pObject = pObject;
  1852.                                 q.pRule = pRule;
  1853.                                 q.pChainedUserEvent = NULL;
  1854.                                 q.pChainedObjectEvent = NULL;
  1855.                                 pQueryEvents->insert(std::make_pair(fDelayTime, q));
  1856.                         }
  1857.                 }
  1858.         }
  1859.  
  1860.         if (!pMinRule)
  1861.                 return 0;
  1862.  
  1863.         // is querying only?
  1864.         if (pQueryEvents)
  1865.                 return -1;
  1866.  
  1867.         int id = GetAISystem()->AllocGoalPipeId();
  1868.         UseSmartObject(pUser, pObject, pMinRule, id, bHighPriority);
  1869.         return !pMinRule->sAction.empty() && pMinRule->eActionType != eAT_None ? id : -1;
  1870. }
  1871.  
  1872. int CSmartObjectManager::TriggerEventUser(const char* sEventName, CSmartObject* pUser, QueryEventMap* pQueryEvents, IEntity** ppObjectEntity, const Vec3* pExtraPoint, bool bHighPriority /*=false*/)
  1873. {
  1874.         *ppObjectEntity = NULL;
  1875.         CSmartObject* pObject = NULL;
  1876.  
  1877.         // check the rules for all objects
  1878.  
  1879.         float minDelay = FLT_MAX;
  1880.         CCondition* pMinRule = NULL;
  1881.         CSmartObject* pMinObject = NULL;
  1882.         CEvent* pEvent = String2Event(sEventName);
  1883.  
  1884.         CSmartObjectClasses::iterator itUserClasses, itUserClassesEnd = pUser->GetClasses().end();
  1885.         for (itUserClasses = pUser->GetClasses().begin(); itUserClasses != itUserClassesEnd; ++itUserClasses)
  1886.         {
  1887.                 CSmartObjectClass* pUserClass = *itUserClasses;
  1888.  
  1889.                 MapPtrConditions::iterator itRules, itRulesEnd = pEvent->m_Conditions.upper_bound(pUserClass);
  1890.                 for (itRules = pEvent->m_Conditions.lower_bound(pUserClass); itRules != itRulesEnd; ++itRules)
  1891.                 {
  1892.                         CCondition* pRule = itRules->second;
  1893.                         if (!pRule->bEnabled)
  1894.                                 continue;
  1895.  
  1896.                         // if we already have found a rule with a delay less than min. delay of this rule then ignore this rule
  1897.                         if (!pQueryEvents && minDelay < pRule->fMinDelay)
  1898.                                 continue;
  1899.  
  1900.                         // proceed with next rule if the user doesn't match states
  1901.                         if (!pRule->userStatePattern.Matches(pUser->GetStates()))
  1902.                                 continue;
  1903.  
  1904.                         Vec3 soPos = pUser->GetPos();
  1905.  
  1906.                         CAIActor* pAIActor = pUser->GetAIActor();
  1907.                         IAIObject* pAttTarget = pAIActor ? pAIActor->GetAttentionTarget() : NULL;
  1908.  
  1909.                         // ignore this rule if the user has no attention target and the rule says it's needed
  1910.                         if (!pExtraPoint && !pAttTarget && pRule->fOrientationToTargetLimit < 360.0f)
  1911.                                 continue;
  1912.  
  1913.                         int alertness = pAIActor ? (pAIActor->IsEnabled() ? pAIActor->GetProxy()->GetAlertnessState() : 1000) : 0;
  1914.                         if (alertness > pRule->iMaxAlertness)
  1915.                                 continue; // ignore this rule - too much alerted
  1916.  
  1917.                         // now for this user check all objects matching the rule's conditions
  1918.  
  1919.                         // first check does it maybe operate only on itself
  1920.                         if (!pRule->pObjectClass)
  1921.                         {
  1922.                                 // calculate delay time
  1923.                                 float fDelayTime = CalculateDelayTime(pUser, soPos, pUser, soPos, pRule);
  1924.                                 if (fDelayTime >= 0.0f && fDelayTime <= minDelay)
  1925.                                 {
  1926.                                         // mark this as best
  1927.                                         minDelay = fDelayTime;
  1928.                                         pMinObject = pUser;
  1929.                                         pMinRule = pRule;
  1930.                                 }
  1931.  
  1932.                                 // add it to the query list
  1933.                                 if (pQueryEvents && fDelayTime >= 0.0f)
  1934.                                 {
  1935.                                         CQueryEvent q;
  1936.                                         q.pUser = pUser;
  1937.                                         q.pObject = pUser;
  1938.                                         q.pRule = pRule;
  1939.                                         q.pChainedUserEvent = NULL;
  1940.                                         q.pChainedObjectEvent = NULL;
  1941.                                         pQueryEvents->insert(std::make_pair(fDelayTime, q));
  1942.                                 }
  1943.  
  1944.                                 // proceed with next rule
  1945.                                 continue;
  1946.                         }
  1947.  
  1948.                         // get species and group id of the user
  1949.                         uint8 factionID = IFactionMap::InvalidFactionID;
  1950.                         int groupId = -1;
  1951.                         CAIActor* pUserActor = pUser->GetAIActor();
  1952.                         if (pUserActor)
  1953.                         {
  1954.                                 groupId = pUserActor->GetGroupId();
  1955.                                 factionID = pUserActor->GetFactionID();
  1956.                         }
  1957.  
  1958.                         // adjust pos if pUserHelper is used
  1959.                         Vec3 pos = pRule->pUserHelper ? pUser->GetHelperPos(pRule->pUserHelper) : soPos;
  1960.  
  1961.                         Vec3 bbMin = pos;
  1962.                         Vec3 bbMax = pos;
  1963.  
  1964.                         float limitTo = pRule->fDistanceTo;
  1965.                         // adjust limit if pUserHelper is used
  1966.                         if (pRule->pUserHelper)
  1967.                                 limitTo += pRule->pUserHelper->qt.t.GetLength();
  1968.                         // adjust limit if pObjectHelper is used
  1969.                         if (pRule->pObjectHelper)
  1970.                                 limitTo += pRule->pObjectHelper->qt.t.GetLength();
  1971.                         Vec3 d(limitTo, limitTo, limitTo);
  1972.                         bbMin -= d;
  1973.                         bbMax += d;
  1974.  
  1975.                         // calculate the limit in advance
  1976.                         float orientationToTargetLimit = -2.0f; // unlimited
  1977.                         if (pRule->fOrientationToTargetLimit < 360.0f)
  1978.                         {
  1979.                                 if (pRule->fOrientationToTargetLimit == 0)
  1980.                                         orientationToTargetLimit = 1.0f;
  1981.                                 else
  1982.                                         orientationToTargetLimit = cosf(pRule->fOrientationToTargetLimit / 360.0f * 3.1415926536f);   // limit is expressed as FOV (360 means unlimited)
  1983.  
  1984.                                 if (pRule->fOrientationToTargetLimit < 0)
  1985.                                         orientationToTargetLimit = -orientationToTargetLimit;
  1986.                         }
  1987.  
  1988.                         // check all objects (but not the user) matching with condition's class and state
  1989.                         CSmartObjectClass::MapSmartObjectsByPos& mapObjectByPos = pRule->pObjectClass->m_MapObjectsByPos;
  1990.                         if (mapObjectByPos.empty())
  1991.                                 continue;
  1992.  
  1993.                         CSmartObjectClass::MapSmartObjectsByPos::iterator itObjectByPos, itObjectByPosEnd = mapObjectByPos.upper_bound(bbMax.x);
  1994.                         for (itObjectByPos = mapObjectByPos.lower_bound(bbMin.x); itObjectByPos != itObjectByPosEnd; ++itObjectByPos)
  1995.                         {
  1996.                                 pObject = itObjectByPos->second;
  1997.  
  1998.                                 // the user can not be the target object!!!
  1999.                                 if (pUser == pObject)
  2000.                                         continue;
  2001.  
  2002.                                 // ignore hidden entities
  2003.                                 if (pObject->IsHidden())
  2004.                                         continue;
  2005.  
  2006.                                 // Check range first since it could be faster
  2007.                                 Vec3 objectPos = pRule->pObjectHelper ? pObject->GetHelperPos(pRule->pObjectHelper) : pObject->GetPos();
  2008.                                 assert(bbMin.x - limitTo <= objectPos.x && objectPos.x <= bbMax.x + limitTo);
  2009.                                 if (objectPos.y < bbMin.y || objectPos.y > bbMax.y ||
  2010.                                     objectPos.z < bbMin.z || objectPos.z > bbMax.z)
  2011.                                         continue;
  2012.  
  2013.                                 // Also check the orientation limit to user's attention target
  2014.                                 if (pRule->fOrientationToTargetLimit < 360.0f)
  2015.                                 {
  2016.                                         Vec3 objectDir = pObject->GetOrientation(pRule->pObjectHelper);
  2017.                                         Vec3 targetDir = (pExtraPoint ? *pExtraPoint : pAttTarget->GetPos()) - objectPos;
  2018.                                         targetDir.NormalizeSafe();
  2019.                                         float dot = objectDir.Dot(targetDir);
  2020.  
  2021.                                         if (pRule->fOrientationToTargetLimit < 0)
  2022.                                                 dot = -dot;
  2023.  
  2024.                                         if (dot < orientationToTargetLimit)
  2025.                                                 continue;
  2026.                                 }
  2027.  
  2028.                                 // add virtual states
  2029.                                 //                              IAIObject* pAIObjectObject = pObject->GetAI();
  2030.                                 CAIActor* pAIObjectObject = pObject->GetAIActor();
  2031.                                 bool attTarget = false;
  2032.                                 bool sameGroupId = false;
  2033.                                 bool sameFaction = false;
  2034.                                 if (pAIObjectObject)
  2035.                                 {
  2036.                                         // check is the object attention target of the user
  2037.                                         attTarget = pAIActor && pAIActor->GetAttentionTarget() == pObject->GetAI();
  2038.                                         if (attTarget)
  2039.                                                 pObject->m_States.insert(m_StateAttTarget);
  2040.  
  2041.                                         // check are the user and the object in the same group and species
  2042.                                         if (groupId >= 0 || factionID != IFactionMap::InvalidFactionID)
  2043.                                         {
  2044.                                                 // check same species
  2045.                                                 uint8 objectFactionID = pAIObjectObject->GetFactionID();
  2046.                                                 sameFaction = factionID == objectFactionID;
  2047.                                                 if (sameFaction)
  2048.                                                 {
  2049.                                                         pObject->m_States.insert(m_StateSameFaction);
  2050.  
  2051.                                                         // if they are same species check are they in same group
  2052.                                                         sameGroupId = groupId == pAIObjectObject->GetGroupId();
  2053.                                                         if (sameGroupId)
  2054.                                                                 pObject->m_States.insert(m_StateSameGroup);
  2055.                                                 }
  2056.                                                 else if ((factionID == IFactionMap::InvalidFactionID) || (objectFactionID == IFactionMap::InvalidFactionID))
  2057.                                                 {
  2058.                                                         // if any of them has no species check are they in same group
  2059.                                                         sameGroupId = groupId == pAIObjectObject->GetGroupId();
  2060.                                                         if (sameGroupId)
  2061.                                                                 pObject->m_States.insert(m_StateSameGroup);
  2062.                                                 }
  2063.                                         }
  2064.                                 }
  2065.  
  2066.                                 // check object's state pattern and then remove virtual states
  2067.                                 bool bMatches = pRule->objectStatePattern.Matches(pObject->GetStates());
  2068.                                 if (attTarget)
  2069.                                         pObject->m_States.erase(m_StateAttTarget);
  2070.                                 if (sameFaction)
  2071.                                         pObject->m_States.erase(m_StateSameFaction);
  2072.                                 if (sameGroupId)
  2073.                                         pObject->m_States.erase(m_StateSameGroup);
  2074.  
  2075.                                 // ignore this object if it doesn't match precondition state
  2076.                                 if (!bMatches)
  2077.                                         continue;
  2078.  
  2079.                                 // calculate delay time
  2080.                                 float fDelayTime = CalculateDelayTime(pUser, pos, pObject, objectPos, pRule);
  2081.                                 if (fDelayTime >= 0.0f && fDelayTime <= minDelay)
  2082.                                 {
  2083.                                         minDelay = fDelayTime;
  2084.                                         pMinObject = pObject;
  2085.                                         pMinRule = pRule;
  2086.                                 }
  2087.  
  2088.                                 // add it to the query list
  2089.                                 if (pQueryEvents && fDelayTime >= 0.0f)
  2090.                                 {
  2091.                                         CQueryEvent q;
  2092.                                         q.pUser = pUser;
  2093.                                         q.pObject = pObject;
  2094.                                         q.pRule = pRule;
  2095.                                         q.pChainedUserEvent = NULL;
  2096.                                         q.pChainedObjectEvent = NULL;
  2097.                                         pQueryEvents->insert(std::make_pair(fDelayTime, q));
  2098.                                 }
  2099.                         }
  2100.                 }
  2101.         }
  2102.  
  2103.         if (!pMinRule)
  2104.                 return 0;
  2105.  
  2106.         // is querying only?
  2107.         if (pQueryEvents)
  2108.                 return -1;
  2109.  
  2110.         int id = GetAISystem()->AllocGoalPipeId();
  2111.         UseSmartObject(pUser, pMinObject, pMinRule, id, bHighPriority);
  2112.         *ppObjectEntity = pMinObject->GetEntity();
  2113.         return !pMinRule->sAction.empty() && pMinRule->eActionType != eAT_None ? id : -1;
  2114. }
  2115.  
  2116. int CSmartObjectManager::TriggerEventObject(const char* sEventName, CSmartObject* pObject, QueryEventMap* pQueryEvents, IEntity** ppUserEntity, const Vec3* pExtraPoint, bool bHighPriority /*=false*/)
  2117. {
  2118.         // WARNING: Virtual states (AttTarget, SameGroup and SameSpecies) in this function are ignored!!!
  2119.         // TODO: Add virtual states...
  2120.  
  2121.         if (!sEventName || !*sEventName)
  2122.                 return 0;
  2123.  
  2124.         *ppUserEntity = NULL;
  2125.         CSmartObject* pUser = NULL;
  2126.  
  2127.         // check the rules on all users
  2128.  
  2129.         float minDelay = FLT_MAX;
  2130.         CCondition* pMinRule = NULL;
  2131.         CSmartObject* pMinUser = NULL;
  2132.         CEvent* pEvent = String2Event(sEventName);
  2133.  
  2134.         MapPtrConditions::iterator itRules, itRulesEnd = pEvent->m_Conditions.end();
  2135.         for (itRules = pEvent->m_Conditions.begin(); itRules != itRulesEnd; ++itRules)
  2136.         {
  2137.                 CCondition* pRule = itRules->second;
  2138.                 if (!pRule->bEnabled)
  2139.                         continue;
  2140.  
  2141.                 // if we already have found a rule with a delay less than min. delay of this rule then ignore this rule
  2142.                 if (!pQueryEvents && minDelay < pRule->fMinDelay)
  2143.                         continue;
  2144.  
  2145.                 // ignore rules which operate only on the user (without an object)
  2146.                 if (!pRule->pObjectClass)
  2147.                         continue;
  2148.  
  2149.                 // ignore rules which object's class is different
  2150.                 if (std::find(pObject->GetClasses().begin(), pObject->GetClasses().end(), pRule->pObjectClass) == pObject->GetClasses().end())
  2151.                         continue;
  2152.  
  2153.                 // proceed with next rule if this one doesn't match object states
  2154.                 if (!pRule->objectStatePattern.Matches(pObject->GetStates()))
  2155.                         continue;
  2156.  
  2157.                 // proceed with next rule if there are no users
  2158.                 CSmartObjectClass::MapSmartObjectsByPos& mapUserByPos = pRule->pUserClass->m_MapObjectsByPos;
  2159.                 if (mapUserByPos.empty())
  2160.                         continue;
  2161.  
  2162.                 // get object's position
  2163.                 Vec3 objectPos = pObject->GetPos();
  2164.  
  2165.                 // adjust pos if pUserHelper is used
  2166.                 Vec3 pos = pRule->pObjectHelper ? pObject->GetHelperPos(pRule->pObjectHelper) : objectPos;
  2167.  
  2168.                 Vec3 bbMin = pos;
  2169.                 Vec3 bbMax = pos;
  2170.  
  2171.                 float limitTo = pRule->fDistanceTo;
  2172.                 // adjust limit if pUserHelper is used
  2173.                 if (pRule->pUserHelper)
  2174.                         limitTo += pRule->pUserHelper->qt.t.GetLength();
  2175.                 // adjust limit if pObjectHelper is used
  2176.                 if (pRule->pObjectHelper)
  2177.                         limitTo += pRule->pObjectHelper->qt.t.GetLength();
  2178.                 Vec3 d(limitTo, limitTo, limitTo);
  2179.                 bbMin -= d;
  2180.                 bbMax += d;
  2181.  
  2182.                 CSmartObjectClass::MapSmartObjectsByPos::iterator itUserByPos, itUserByPosEnd = mapUserByPos.upper_bound(bbMax.x);
  2183.                 for (itUserByPos = mapUserByPos.lower_bound(bbMin.x); itUserByPos != itUserByPosEnd; ++itUserByPos)
  2184.                 {
  2185.                         pUser = itUserByPos->second;
  2186.  
  2187.                         // now check does this user can use the object
  2188.  
  2189.                         // don't let the user to be the object
  2190.                         if (pUser == pObject)
  2191.                                 continue;
  2192.  
  2193.                         // ignore hidden entities
  2194.                         if (pUser->IsHidden())
  2195.                                 continue;
  2196.  
  2197.                         // proceed with next user if this one doesn't match states
  2198.                         if (!pRule->userStatePattern.Matches(pUser->GetStates()))
  2199.                                 continue;
  2200.  
  2201.                         CAIActor* pAIActor = pUser->GetAIActor();
  2202.                         IAIObject* pAttTarget = pAIActor ? pAIActor->GetAttentionTarget() : NULL;
  2203.  
  2204.                         // ignore this user if it has no attention target and the rule says it's needed
  2205.                         if (!pExtraPoint && !pAttTarget && pRule->fOrientationToTargetLimit < 360.0f)
  2206.                                 continue;
  2207.  
  2208.                         int alertness = pAIActor ? (pAIActor->IsEnabled() ? pAIActor->GetProxy()->GetAlertnessState() : 1000) : 0;
  2209.                         if (alertness > pRule->iMaxAlertness)
  2210.                                 continue; // ignore this user - too much alerted
  2211.  
  2212.                         Vec3 userPos = pUser->GetPos();
  2213.  
  2214.                         // adjust pos if pUserHelper is used
  2215.                         userPos = pRule->pUserHelper ? pUser->GetHelperPos(pRule->pUserHelper) : userPos;
  2216.  
  2217.                         assert(bbMin.x - limitTo <= userPos.x && userPos.x <= bbMax.x + limitTo);
  2218.                         if (userPos.y < bbMin.y || userPos.y > bbMax.y ||
  2219.                             userPos.z < bbMin.z || userPos.z > bbMax.z)
  2220.                                 continue;
  2221.  
  2222.                         // check the orientation limit to target
  2223.                         if (pRule->fOrientationToTargetLimit < 360.0f)
  2224.                         {
  2225.                                 float cosLimit = 1.0f;
  2226.                                 if (pRule->fOrientationToTargetLimit != 0)
  2227.                                         cosLimit = cosf(pRule->fOrientationToTargetLimit / 360.0f * 3.1415926536f);   // limit is expressed as FOV (360 means unlimited)
  2228.  
  2229.                                 Vec3 objectDir = pObject->GetOrientation(pRule->pObjectHelper);
  2230.                                 Vec3 targetDir = (pExtraPoint ? *pExtraPoint : pAttTarget->GetPos()) - pos;
  2231.                                 targetDir.NormalizeSafe();
  2232.                                 float dot = objectDir.Dot(targetDir);
  2233.  
  2234.                                 if (pRule->fOrientationToTargetLimit < 0)
  2235.                                 {
  2236.                                         dot = -dot;
  2237.                                         cosLimit = -cosLimit;
  2238.                                 }
  2239.  
  2240.                                 if (dot < cosLimit)
  2241.                                         continue;
  2242.                         }
  2243.  
  2244.                         // calculate delay time
  2245.                         float fDelayTime = CalculateDelayTime(pUser, userPos, pObject, pos, pRule);
  2246.                         if (fDelayTime >= 0.0f && fDelayTime <= minDelay)
  2247.                         {
  2248.                                 minDelay = fDelayTime;
  2249.                                 pMinUser = pUser;
  2250.                                 pMinRule = pRule;
  2251.                         }
  2252.  
  2253.                         // add it to the query list
  2254.                         if (pQueryEvents && fDelayTime >= 0.0f)
  2255.                         {
  2256.                                 CQueryEvent q;
  2257.                                 q.pUser = pUser;
  2258.                                 q.pObject = pObject;
  2259.                                 q.pRule = pRule;
  2260.                                 q.pChainedUserEvent = NULL;
  2261.                                 q.pChainedObjectEvent = NULL;
  2262.                                 pQueryEvents->insert(std::make_pair(fDelayTime, q));
  2263.                         }
  2264.                 }
  2265.         }
  2266.  
  2267.         if (!pMinRule)
  2268.                 return 0;
  2269.  
  2270.         // is querying only?
  2271.         if (pQueryEvents)
  2272.                 return -1;
  2273.  
  2274.         int id = GetAISystem()->AllocGoalPipeId();
  2275.         UseSmartObject(pMinUser, pObject, pMinRule, id, bHighPriority);
  2276.         *ppUserEntity = pMinUser->GetEntity();
  2277.         return !pMinRule->sAction.empty() && pMinRule->eActionType != eAT_None ? id : -1;
  2278. }
  2279.  
  2280. struct SOUpdateStats
  2281. {
  2282.         int  classes;
  2283.         int  users;
  2284.         int  pairs;
  2285.  
  2286.         int  hiddenUsers;
  2287.         int  hiddenObjects;
  2288.  
  2289.         int  totalRules;
  2290.         int  ignoredNotNeededRules;
  2291.         int  ignoredAttTargetRules;
  2292.         int  ignoredStatesNotMatchingRules;
  2293.  
  2294.         int  appliedUserOnlyRules;
  2295.         int  appliedUserObjectRules;
  2296.  
  2297.         int  ignoredNotInRangeObjects;
  2298.         int  ignoredAttTargetNotInRangeObjects;
  2299.         int  ignoredStateDoesntMatchObjects;
  2300.  
  2301.         void reset()
  2302.         {
  2303.                 classes = 0;
  2304.                 users = 0;
  2305.                 pairs = 0;
  2306.  
  2307.                 hiddenUsers = 0;
  2308.                 hiddenObjects = 0;
  2309.  
  2310.                 totalRules = 0;
  2311.                 ignoredNotNeededRules = 0;
  2312.                 ignoredAttTargetRules = 0;
  2313.                 ignoredStatesNotMatchingRules = 0;
  2314.  
  2315.                 appliedUserOnlyRules = 0;
  2316.                 appliedUserObjectRules = 0;
  2317.  
  2318.                 ignoredNotInRangeObjects = 0;
  2319.                 ignoredAttTargetNotInRangeObjects = 0;
  2320.                 ignoredStateDoesntMatchObjects = 0;
  2321.         }
  2322. };
  2323.  
  2324. SOUpdateStats currentUpdateStats;
  2325.  
  2326. void CSmartObjectManager::Update()
  2327. {
  2328.         FUNCTION_PROFILER(gEnv->pSystem, PROFILE_AI)
  2329.  
  2330.         currentUpdateStats.reset();
  2331.  
  2332.         static int totalNumPairsToUpdate = 0;
  2333.         static int maxNumPairsUpdatedPerFrame = 100;
  2334.         int pairsUpdatedThisFrame = 0;
  2335.  
  2336.         if (CSmartObjectClass::g_itAllUserClasses == CSmartObjectClass::g_AllUserClasses.end())
  2337.         {
  2338.                 // this is the end of a full cycle and beginning of a new one
  2339.                 maxNumPairsUpdatedPerFrame = (maxNumPairsUpdatedPerFrame + (totalNumPairsToUpdate / 6)) / 2 + 6;
  2340.                 totalNumPairsToUpdate = 0;
  2341.  
  2342.                 CSmartObjectClass::g_itAllUserClasses = CSmartObjectClass::g_AllUserClasses.begin();
  2343.                 if (CSmartObjectClass::g_itAllUserClasses != CSmartObjectClass::g_AllUserClasses.end())
  2344.                         (*CSmartObjectClass::g_itAllUserClasses)->FirstObject();
  2345.         }
  2346.  
  2347.         while (pairsUpdatedThisFrame < maxNumPairsUpdatedPerFrame &&
  2348.                CSmartObjectClass::g_itAllUserClasses != CSmartObjectClass::g_AllUserClasses.end())
  2349.         {
  2350.                 CSmartObjectClass* pClass = *CSmartObjectClass::g_itAllUserClasses;
  2351.                 ++currentUpdateStats.classes;
  2352.  
  2353.                 while (pairsUpdatedThisFrame < maxNumPairsUpdatedPerFrame)
  2354.                 {
  2355.                         CSmartObject* pSmartObject = pClass->NextVisibleObject();
  2356.                         if (pSmartObject == NULL)
  2357.                         {
  2358.                                 // continue with the next class
  2359.                                 ++CSmartObjectClass::g_itAllUserClasses;
  2360.                                 if (CSmartObjectClass::g_itAllUserClasses != CSmartObjectClass::g_AllUserClasses.end())
  2361.                                         (*CSmartObjectClass::g_itAllUserClasses)->FirstObject();
  2362.  
  2363.                                 break;
  2364.                         }
  2365.                         ++currentUpdateStats.users;
  2366.  
  2367.                         int numPairsProcessed = Process(pSmartObject, pClass);
  2368.                         pairsUpdatedThisFrame += numPairsProcessed;
  2369.                         currentUpdateStats.pairs += numPairsProcessed;
  2370.  
  2371.                         // update LookAt smart object position, but only if this is the last object's user class
  2372.                         CSmartObjectClasses& classes = pSmartObject->GetClasses();
  2373.                         int i = classes.size();
  2374.                         while (i--)
  2375.                         {
  2376.                                 CSmartObjectClass* current = classes[i];
  2377.                                 if (pClass == current)
  2378.                                 {
  2379.                                         CPipeUser* pPipeUser = pSmartObject->GetPipeUser();
  2380.                                         if (pPipeUser)
  2381.                                         {
  2382.                                                 if (pSmartObject->m_fLookAtLimit)
  2383.                                                         pPipeUser->m_posLookAtSmartObject = pSmartObject->m_vLookAtPos;
  2384.                                                 else
  2385.                                                         pPipeUser->m_posLookAtSmartObject.zero();
  2386.                                         }
  2387.                                         pSmartObject->m_fLookAtLimit = 0.0f;
  2388.                                         break; // exit the loop! this was the last user class.
  2389.                                 }
  2390.                                 if (current->IsSmartObjectUser())
  2391.                                         break; // exit the loop! some other class is the last user class.
  2392.                         }
  2393.                 }
  2394.         }
  2395.  
  2396.         totalNumPairsToUpdate += pairsUpdatedThisFrame;
  2397. }
  2398.  
  2399. int CSmartObjectManager::Process(CSmartObject* pSmartObjectUser, CSmartObjectClass* pClass)
  2400. {
  2401.         FUNCTION_PROFILER(GetISystem(), PROFILE_AI);
  2402.  
  2403.         AIAssert(pSmartObjectUser);
  2404.  
  2405.         if (pSmartObjectUser->IsHidden())
  2406.         {
  2407.                 // Ignore hidden entities - except the player!
  2408.                 // Allows the camera to trigger objects in editor AI/Physics mode
  2409.                 // Very few entities are registered but hidden, so we can afford this test
  2410.                 IAIObject* pPlayer = GetAISystem()->GetPlayer();
  2411.                 if (!pPlayer || pSmartObjectUser->GetEntityId() != pPlayer->GetEntityID())
  2412.                 {
  2413.                         ++currentUpdateStats.hiddenUsers;
  2414.                         return 1;
  2415.                 }
  2416.         }
  2417.  
  2418.         CAIActor* pAIActor = pSmartObjectUser->GetAIActor();
  2419.         // don't do this here! it should be done in Update() after processing the last class
  2420.         //if ( pPipeUser )
  2421.         //      pPipeUser->m_posLookAtSmartObject.zero();
  2422.  
  2423.         if (pAIActor)
  2424.         {
  2425.                 if ((pAIActor->IsEnabled() == false) || (pAIActor->IsUpdatedOnce() == false))
  2426.                 {
  2427.                         // ignore not enabled puppet-users
  2428.                         ++currentUpdateStats.hiddenUsers;
  2429.                         return 1;
  2430.                 }
  2431.         }
  2432.  
  2433.         // this entity is a smart object user - update his potential smart object targets
  2434.         CTimeValue lastUpdateTime(0ll);
  2435.         CSmartObject::MapTimesByClass::iterator itFind = pSmartObjectUser->m_mapLastUpdateTimes.find(pClass);
  2436.         if (itFind != pSmartObjectUser->m_mapLastUpdateTimes.end())
  2437.                 lastUpdateTime = itFind->second;
  2438.  
  2439.         CTimeValue now = GetAISystem()->GetFrameStartTime();
  2440.  
  2441.         int64 timeElapsedMs = (now - lastUpdateTime).GetMilliSecondsAsInt64();
  2442.         if (lastUpdateTime.GetMilliSecondsAsInt64() == 0)
  2443.                 timeElapsedMs = 0;
  2444.         else if (timeElapsedMs > 200)
  2445.                 timeElapsedMs = 200;
  2446.  
  2447.         float timeElapsedMsToFloat = (float)(timeElapsedMs);
  2448.  
  2449.         pSmartObjectUser->m_mapLastUpdateTimes[pClass] = now;
  2450.  
  2451.         int result = 1;
  2452.  
  2453.         uint8 factionID = IFactionMap::InvalidFactionID;
  2454.         int groupId = -1;
  2455.         CAIActor* pUserActor = pSmartObjectUser->GetAIActor();
  2456.         if (pUserActor)
  2457.         {
  2458.                 groupId = pUserActor->GetGroupId();
  2459.                 factionID = pUserActor->GetFactionID();
  2460.         }
  2461.  
  2462.         IAIObject* pAttTarget = pAIActor ? pAIActor->GetAttentionTarget() : NULL;
  2463.         int alertness = pAIActor ? CLAMP(pAIActor->GetProxy()->GetAlertnessState(), 0, 2) : 0;
  2464.  
  2465.         Vec3 soPos = pSmartObjectUser->GetPos();
  2466.  
  2467.         m_tmpVecDelayTimes.clear();
  2468.  
  2469.         float fTimeElapsed = timeElapsedMsToFloat * 0.001f;
  2470.  
  2471.         // check all conditions matching with his class and state
  2472.         //MapConditions::iterator itConditions = m_Conditions.find( pClass );
  2473.         // optimized: use only the active rules
  2474.         CSmartObjectClass::VectorRules& activeRules = pClass->m_vActiveUpdateRules[alertness];
  2475.         CSmartObjectClass::VectorRules::iterator itConditions, itConditionsEnd = activeRules.end();
  2476.  
  2477.         for (itConditions = activeRules.begin(); itConditions != itConditionsEnd; ++itConditions)
  2478.         {
  2479.                 CCondition* pCondition = *itConditions;
  2480.                 ++currentUpdateStats.totalRules;
  2481.  
  2482.                 // optimized: active rules have these already culled out
  2483.                 /*
  2484.                    if ( !pCondition->bEnabled || pCondition->iMaxAlertness < alertness || pCondition->iRuleType != 0 )
  2485.                    {
  2486.                    //++currentUpdateStats.ignoredDisabledIdleNavRules;
  2487.                    continue;
  2488.                    }
  2489.                    // ignore event rules
  2490.                    if ( !pCondition->sEvent.empty() )
  2491.                    {
  2492.                    //++currentUpdateStats.ignoredEventRules;
  2493.                    continue;
  2494.                    }
  2495.                  */
  2496.  
  2497.                 // optimized: ignore the rules if there are no instances of the object class
  2498.                 if (pCondition->pObjectClass && pCondition->pObjectClass->IsNeeded() == false)
  2499.                 {
  2500.                         ++currentUpdateStats.ignoredNotNeededRules;
  2501.                         continue;
  2502.                 }
  2503.  
  2504.                 // ignore this rule if the user has no attention target and the rule says it's needed
  2505.                 if (!pAttTarget && pCondition->fOrientationToTargetLimit < 360.0f)
  2506.                 {
  2507.                         ++currentUpdateStats.ignoredAttTargetRules;
  2508.                         continue;
  2509.                 }
  2510.  
  2511.                 // go to next if this one doesn't match user's states
  2512.                 if (!pCondition->userStatePattern.Matches(pSmartObjectUser->GetStates()))
  2513.                 {
  2514.                         ++currentUpdateStats.ignoredStatesNotMatchingRules;
  2515.                         continue;
  2516.                 }
  2517.  
  2518.                 // check does it operate only on itself
  2519.                 if (!pCondition->pObjectClass)
  2520.                 {
  2521.                         // count objects because this method returns the number of relations processed
  2522.                         ++result;
  2523.                         ++currentUpdateStats.appliedUserOnlyRules;
  2524.  
  2525.                         // calculated delta times should be stored only for now.
  2526.                         // later existing events will be updated and for new objects new events will be added
  2527.                         float fDelayTime = CalculateDelayTime(pSmartObjectUser, soPos, pSmartObjectUser, soPos, pCondition);
  2528.                         if (fDelayTime > 0.0f)
  2529.                         {
  2530.                                 PairObjectCondition poc(pSmartObjectUser, pCondition);
  2531.                                 m_tmpVecDelayTimes.push_back(std::make_pair(poc, fTimeElapsed / fDelayTime));
  2532.                         }
  2533.                         else if (fDelayTime == 0.0f)
  2534.                         {
  2535.                                 PairObjectCondition poc(pSmartObjectUser, pCondition);
  2536.                                 m_tmpVecDelayTimes.push_back(std::make_pair(poc, 1.0f));
  2537.                         }
  2538.  
  2539.                         // this condition is done
  2540.                         continue;
  2541.                 }
  2542.  
  2543.                 // adjust pos if pUserHelper is used
  2544.                 Vec3 pos = pCondition->pUserHelper ? pSmartObjectUser->GetHelperPos(pCondition->pUserHelper) : soPos;
  2545.  
  2546.                 Vec3 bbMin = pos;
  2547.                 Vec3 bbMax = pos;
  2548.  
  2549.                 float limitTo = pCondition->fDistanceTo;
  2550.                 // adjust limit if pUserHelper is used
  2551.                 if (pCondition->pUserHelper)
  2552.                         limitTo += pCondition->pUserHelper->qt.t.GetLength();
  2553.                 // adjust limit if pObjectHelper is used
  2554.                 if (pCondition->pObjectHelper)
  2555.                         limitTo += pCondition->pObjectHelper->qt.t.GetLength();
  2556.                 Vec3 d(limitTo, limitTo, limitTo);
  2557.                 bbMin -= d;
  2558.                 bbMax += d;
  2559.  
  2560.                 // calculate the limit in advance
  2561.                 float orientationToTargetLimit = -2.0f; // unlimited
  2562.                 if (pCondition->fOrientationToTargetLimit < 360.0f)
  2563.                 {
  2564.                         if (pCondition->fOrientationToTargetLimit == 0)
  2565.                                 orientationToTargetLimit = 1.0f;
  2566.                         else
  2567.                                 orientationToTargetLimit = cosf(pCondition->fOrientationToTargetLimit / 360.0f * 3.1415926536f);   // limit is expressed as FOV (360 means unlimited)
  2568.  
  2569.                         if (pCondition->fOrientationToTargetLimit < 0)
  2570.                                 orientationToTargetLimit = -orientationToTargetLimit;
  2571.                 }
  2572.  
  2573.                 // check all objects (but not the user) matching with condition's class and state
  2574.                 CSmartObjectClass::MapSmartObjectsByPos& mapObjectsByPos = pCondition->pObjectClass->m_MapObjectsByPos;
  2575.                 if (mapObjectsByPos.empty())
  2576.                         continue;
  2577.  
  2578.                 CSmartObjectClass::MapSmartObjectsByPos::iterator itByPos = mapObjectsByPos.lower_bound(bbMin.x);
  2579.                 for (; itByPos != mapObjectsByPos.end() && itByPos->first <= bbMax.x; ++itByPos)
  2580.                 {
  2581.                         CSmartObject* pSmartObject = itByPos->second;
  2582.                         ++currentUpdateStats.appliedUserObjectRules;
  2583.  
  2584.                         // the user can not be the target object!!!
  2585.                         if (pSmartObjectUser == pSmartObject)
  2586.                                 continue;
  2587.  
  2588.                         // ignore hidden entities
  2589.                         if (pSmartObject->IsHidden())
  2590.                         {
  2591.                                 ++currentUpdateStats.hiddenObjects;
  2592.                                 continue;
  2593.                         }
  2594.  
  2595.                         // Check range first since it could be faster
  2596.                         Vec3 objectPos = pCondition->pObjectHelper ? pSmartObject->GetHelperPos(pCondition->pObjectHelper) : pSmartObject->GetPos();
  2597.                         if (objectPos.IsZero())
  2598.                                 continue;
  2599.                         assert(objectPos.x == 0 || bbMin.x - limitTo <= objectPos.x && objectPos.x <= bbMax.x + limitTo);
  2600.                         if (objectPos.y < bbMin.y || objectPos.y > bbMax.y ||
  2601.                             objectPos.z < bbMin.z || objectPos.z > bbMax.z)
  2602.                         {
  2603.                                 ++currentUpdateStats.ignoredNotInRangeObjects;
  2604.                                 continue;
  2605.                         }
  2606.  
  2607.                         // Also check the orientation limit to user's attention target
  2608.                         if (pCondition->fOrientationToTargetLimit < 360.0f)
  2609.                         {
  2610.                                 Vec3 objectDir = pSmartObject->GetOrientation(pCondition->pObjectHelper);
  2611.                                 Vec3 targetDir = pAttTarget->GetPos() - objectPos;
  2612.                                 targetDir.NormalizeSafe