BVB Source Codes

CRYENGINE Show PerceptionManager.cpp Source code

Return Download CRYENGINE: download PerceptionManager.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:   PerceptionManager.cpp
  6.    $Id$
  7.    Description:
  8.  
  9.    -------------------------------------------------------------------------
  10.    History:
  11.    - 2008                               : Created by Mikko Mononen
  12.  
  13.  *********************************************************************/
  14.  
  15. #include "StdAfx.h"
  16. #include "PerceptionManager.h"
  17. #include "CAISystem.h"
  18. #include "Puppet.h"
  19. #include "AIPlayer.h"
  20. #include "AIVehicle.h"
  21. #include "DebugDrawContext.h"
  22.  
  23. // AI Stimulus names for debugging
  24. static const char* g_szAIStimulusType[AISTIM_LAST] =
  25. {
  26.         "SOUND",
  27.         "COLLISION",
  28.         "EXPLOSION",
  29.         "BULLET_WHIZZ",
  30.         "BULLET_HIT",
  31.         "GRENADE"
  32. };
  33. static const char* g_szAISoundStimType[AISOUND_LAST] =
  34. {
  35.         " GENERIC",
  36.         " COLLISION",
  37.         " COLLISION_LOUD",
  38.         " MOVEMENT",
  39.         " MOVEMENT_LOUD",
  40.         " WEAPON",
  41.         " EXPLOSION"
  42. };
  43. static const char* g_szAIGrenadeStimType[AIGRENADE_LAST] =
  44. {
  45.         " THROWN",
  46.         " COLLISION",
  47.         " FLASH_BANG",
  48.         " SMOKE"
  49. };
  50.  
  51. std::vector<CAIObject*> CPerceptionManager::s_targetEntities;
  52. std::vector<CAIVehicle*> CPerceptionManager::s_playerVehicles;
  53.  
  54. //===================================================================
  55. // CPerceptionManager
  56. //===================================================================
  57. CPerceptionManager::CPerceptionManager()
  58. {
  59.         Reset(IAISystem::RESET_INTERNAL);
  60.         for (unsigned i = 0; i < AI_MAX_STIMULI; ++i)
  61.         {
  62.                 m_stimulusTypes[i].Reset();
  63.         }
  64. }
  65.  
  66. //===================================================================
  67. // CPerceptionManager
  68. //===================================================================
  69. CPerceptionManager::~CPerceptionManager()
  70. {
  71. }
  72.  
  73. //===================================================================
  74. // InitCommonTypeDescs
  75. //===================================================================
  76. void CPerceptionManager::InitCommonTypeDescs()
  77. {
  78.         SAIStimulusTypeDesc desc;
  79.  
  80.         // Sound
  81.         desc.Reset();
  82.         desc.SetName("Sound");
  83.         desc.processDelay = 0.15f;
  84.         desc.duration[AISOUND_GENERIC] = 2.0f;
  85.         desc.duration[AISOUND_COLLISION] = 4.0f;
  86.         desc.duration[AISOUND_COLLISION_LOUD] = 4.0f;
  87.         desc.duration[AISOUND_MOVEMENT] = 2.0f;
  88.         desc.duration[AISOUND_MOVEMENT_LOUD] = 4.0f;
  89.         desc.duration[AISOUND_WEAPON] = 4.0f;
  90.         desc.duration[AISOUND_EXPLOSION] = 6.0f;
  91.         desc.filterTypes = 0;
  92.         desc.nFilters = 0;
  93.         RegisterStimulusDesc(AISTIM_SOUND, desc);
  94.  
  95.         // Collision
  96.         desc.Reset();
  97.         desc.SetName("Collision");
  98.         desc.processDelay = 0.15f;
  99.         desc.duration[AICOL_SMALL] = 7.0f;
  100.         desc.duration[AICOL_MEDIUM] = 7.0f;
  101.         desc.duration[AICOL_LARGE] = 7.0f;
  102.         desc.filterTypes = (1 << AISTIM_COLLISION) | (1 << AISTIM_EXPLOSION);
  103.         desc.nFilters = 2;
  104.         desc.filters[0].Set(AISTIM_COLLISION, 0, AISTIMFILTER_MERGE_AND_DISCARD, 0.9f); // Merge nearby collisions
  105.         desc.filters[1].Set(AISTIM_EXPLOSION, 0, AISTIMFILTER_DISCARD, 2.5f);           // Discard collision near explosions
  106.         RegisterStimulusDesc(AISTIM_COLLISION, desc);
  107.  
  108.         // Explosion
  109.         desc.Reset();
  110.         desc.SetName("Explosion");
  111.         desc.processDelay = 0.15f;
  112.         desc.duration[0] = 7.0f;
  113.         desc.filterTypes = (1 << AISTIM_EXPLOSION);
  114.         desc.nFilters = 1;
  115.         desc.filters[0].Set(AISTIM_EXPLOSION, 0, AISTIMFILTER_MERGE_AND_DISCARD, 0.5f); // Merge nearby explosions
  116.         RegisterStimulusDesc(AISTIM_EXPLOSION, desc);
  117.  
  118.         // Bullet Whizz
  119.         desc.Reset();
  120.         desc.SetName("BulletWhizz");
  121.         desc.processDelay = 0.01f;
  122.         desc.duration[0] = 0.5f;
  123.         desc.filterTypes = 0;
  124.         desc.nFilters = 0;
  125.         RegisterStimulusDesc(AISTIM_BULLET_WHIZZ, desc);
  126.  
  127.         // Bullet Hit
  128.         desc.Reset();
  129.         desc.SetName("BulletHit");
  130.         desc.processDelay = 0.15f;
  131.         desc.duration[0] = 0.5f;
  132.         desc.filterTypes = (1 << AISTIM_BULLET_HIT);
  133.         desc.nFilters = 1;
  134.         desc.filters[0].Set(AISTIM_BULLET_HIT, 0, AISTIMFILTER_MERGE_AND_DISCARD, 0.5f); // Merge nearby hits
  135.         RegisterStimulusDesc(AISTIM_BULLET_HIT, desc);
  136.  
  137.         // Grenade
  138.         desc.Reset();
  139.         desc.SetName("Grenade");
  140.         desc.processDelay = 0.15f;
  141.         desc.duration[AIGRENADE_THROWN] = 6.0f;
  142.         desc.duration[AIGRENADE_COLLISION] = 6.0f;
  143.         desc.duration[AIGRENADE_FLASH_BANG] = 6.0f;
  144.         desc.duration[AIGRENADE_SMOKE] = 6.0f;
  145.         desc.filterTypes = (1 << AISTIM_GRENADE);
  146.         desc.nFilters = 1;
  147.         desc.filters[0].Set(AISTIM_GRENADE, AIGRENADE_COLLISION, AISTIMFILTER_MERGE_AND_DISCARD, 1.0f); // Merge nearby collisions
  148.         RegisterStimulusDesc(AISTIM_GRENADE, desc);
  149.  
  150. }
  151.  
  152. bool CPerceptionManager::RegisterStimulusDesc(EAIStimulusType type, const SAIStimulusTypeDesc& desc)
  153. {
  154.         m_stimulusTypes[type] = desc;
  155.         return true;
  156. }
  157.  
  158. //===================================================================
  159. // Reset
  160. //===================================================================
  161. void CPerceptionManager::Reset(IAISystem::EResetReason reason)
  162. {
  163.         /*      m_visChecks = 0;
  164.            m_visChecksMax = 0;
  165.            m_visChecksRays = 0;
  166.            m_visChecksRaysMax = 0;
  167.            m_visChecksHistoryHead = 0;
  168.            m_visChecksHistorySize = 0;
  169.            m_nRaysThisUpdateFrame = 0;
  170.            m_maxStimsPerUpdate = 0;*/
  171.  
  172.         m_visBroadPhaseDt = 0;
  173.  
  174.         if (reason == IAISystem::RESET_UNLOAD_LEVEL)
  175.         {
  176.                 stl::free_container(m_incomingStimuli);
  177.                 for (unsigned int i = 0; i < AI_MAX_STIMULI; ++i)
  178.                 {
  179.                         stl::free_container(m_stimuli[i]);
  180.                         m_ignoreStimuliFrom[i].clear();
  181.                 }
  182.  
  183.                 if (m_eventListeners.empty())
  184.                         stl::free_container(m_eventListeners);
  185.  
  186.                 stl::free_container(s_targetEntities);
  187.                 stl::free_container(s_playerVehicles);
  188.         }
  189.         else
  190.         {
  191.                 for (unsigned i = 0; i < AI_MAX_STIMULI; ++i)
  192.                 {
  193.                         m_stimuli[i].reserve(64);
  194.                         m_stimuli[i].clear();
  195.                         m_ignoreStimuliFrom[i].clear();
  196.                 }
  197.                 m_incomingStimuli.reserve(64);
  198.                 m_incomingStimuli.clear();
  199.                 InitCommonTypeDescs();
  200.                 s_targetEntities.reserve(10);
  201.                 s_playerVehicles.reserve(10);
  202.         }
  203. }
  204.  
  205. //===================================================================
  206. // UpdatePerception
  207. //===================================================================
  208. bool CPerceptionManager::UpdatePerception(CAIActor* pAIActor, std::vector<CAIObject*>& priorityTargets)
  209. {
  210.         FUNCTION_PROFILER(gEnv->pSystem, PROFILE_AI);
  211.  
  212.         m_stats.trackers[PERFTRACK_UPDATES].Inc();
  213.  
  214.         if (pAIActor->IsEnabled() && pAIActor->IsPerceptionEnabled())
  215.         {
  216.                 const Vec3& vAIActorPos = pAIActor->GetPos();
  217.  
  218.                 if (!priorityTargets.empty())
  219.                 {
  220.                         FRAME_PROFILER("AIPlayerVisibilityCheck", gEnv->pSystem, PROFILE_AI);
  221.  
  222.                         // Priority targets.
  223.                         for (unsigned i = 0, ni = priorityTargets.size(); i < ni; ++i)
  224.                         {
  225.                                 CAIObject* pTarget = priorityTargets[i];
  226.                                 if (!pAIActor->IsHostile(pTarget))
  227.                                         continue;
  228.  
  229.                                 if (pAIActor->IsDevalued(pTarget))
  230.                                         continue;
  231.  
  232.                                 if (!pAIActor->CanAcquireTarget(pTarget))
  233.                                         continue;
  234.  
  235.                                 IAIObject::EFieldOfViewResult viewResult = pAIActor->IsObjectInFOV(pTarget);
  236.                                 if (IAIObject::eFOV_Outside == viewResult)
  237.                                         continue;
  238.  
  239.                                 pTarget->SetObservable(true);
  240.  
  241.                                 m_stats.trackers[PERFTRACK_VIS_CHECKS].Inc();
  242.  
  243.                                 const Vec3& vTargetPos = pTarget->GetPos();
  244.  
  245.                                 // TODO(M谩rcio): Implement
  246.                                 // To make it generic, can have the vision map store 2 collision flag fields and alternate them in case of failure.
  247.                                 /*
  248.                                    bool skipSoftCover = false;
  249.                                    // See grenades through vegetation.
  250.                                    if (pTarget->GetType() == AIOBJECT_GRENADE || pTarget->GetType() == AIOBJECT_RPG)
  251.                                    skipSoftCover = true;
  252.  
  253.                                    // See live and memory target through vegetation.
  254.                                    if ((pAIActor->GetAttentionTargetType() == AITARGET_VISUAL ||
  255.                                    pAIActor->GetAttentionTargetType() == AITARGET_MEMORY) &&
  256.                                    pAIActor->GetAttentionTargetThreat() == AITHREAT_AGGRESSIVE)
  257.                                    {
  258.                                    const float distSq = Distance::Point_PointSq(vAIActorPos, vTargetPos);
  259.                                    skipSoftCover = (distSq < sqr(pPuppet->GetParameters().m_fMeleeRange * 1.2f));
  260.                                    }*/
  261.  
  262.                                 bool visible = false;
  263.                                 if (!RayOcclusionPlaneIntersection(vAIActorPos, vTargetPos))
  264.                                         visible = pAIActor->CanSee(pTarget->GetVisionID());
  265.  
  266.                                 if (visible)
  267.                                 {
  268.                                         // Notify visual perception.
  269.                                         SAIEVENT event;
  270.                                         event.sourceId = pTarget->GetPerceivedEntityID();
  271.                                         event.bFuzzySight = (viewResult == IAIObject::eFOV_Secondary);
  272.                                         event.vPosition = vTargetPos;
  273.                                         pAIActor->Event(AIEVENT_ONVISUALSTIMULUS, &event);
  274.                                 }
  275.  
  276.                                 switch (pTarget->GetType())
  277.                                 {
  278.                                 case AIOBJECT_PLAYER:
  279.                                 case AIOBJECT_ACTOR:
  280.                                         if (pTarget->IsEnabled())
  281.                                         {
  282.                                                 if (pAIActor->GetAttentionTarget() == pTarget)
  283.                                                 {
  284.                                                         const float distSq = Distance::Point_PointSq(vAIActorPos, vTargetPos);
  285.                                                         if (distSq < sqr(pAIActor->GetParameters().m_fMeleeRange))
  286.                                                         {
  287.                                                                 if (pAIActor->CloseContactEnabled())
  288.                                                                 {
  289.                                                                         pAIActor->SetSignal(1, "OnCloseContact", pTarget->GetEntity(), 0, gAIEnv.SignalCRCs.m_nOnCloseContact);
  290.                                                                         pAIActor->SetCloseContact(true);
  291.                                                                 }
  292.                                                         }
  293.                                                 }
  294.                                         }
  295.                                 }
  296.                         }
  297.                 }
  298.  
  299.                 {
  300.                         FRAME_PROFILER("ProbableTargets", gEnv->pSystem, PROFILE_AI);
  301.  
  302.                         // Probable targets.
  303.                         for (unsigned i = 0, ni = pAIActor->m_probableTargets.size(); i < ni; ++i)
  304.                         {
  305.                                 CAIObject* pProbTarget = pAIActor->m_probableTargets[i];
  306.  
  307.                                 CPuppet* pTargetPuppet = pProbTarget->CastToCPuppet();
  308.                                 if (pTargetPuppet)
  309.                                 {
  310.                                         if (pTargetPuppet->m_Parameters.m_bInvisible || pAIActor->IsDevalued(pProbTarget))
  311.                                                 continue;
  312.                                 }
  313.  
  314.                                 if (!pAIActor->CanAcquireTarget(pProbTarget))
  315.                                         continue;
  316.  
  317.                                 if (!pAIActor->IsHostile(pProbTarget))
  318.                                         continue;
  319.  
  320.                                 IAIObject::EFieldOfViewResult viewResult = pAIActor->IsObjectInFOV(pProbTarget);
  321.                                 if (IAIObject::eFOV_Outside == viewResult)
  322.                                         continue;
  323.  
  324.                                 pProbTarget->SetObservable(true);
  325.  
  326.                                 const Vec3& vProbTargetPos = pProbTarget->GetPos();
  327.  
  328.                                 bool visible = false;
  329.                                 if (!RayOcclusionPlaneIntersection(vAIActorPos, vProbTargetPos))
  330.                                         visible = pAIActor->CanSee(pProbTarget->GetVisionID());
  331.  
  332.                                 if (visible)
  333.                                 {
  334.                                         SAIEVENT event;
  335.                                         event.sourceId = pProbTarget->GetPerceivedEntityID();
  336.                                         event.bFuzzySight = (viewResult == IAIObject::eFOV_Secondary);
  337.                                         event.fThreat = 1.f;
  338.                                         event.vPosition = vProbTargetPos;
  339.                                         pAIActor->Event(AIEVENT_ONVISUALSTIMULUS, &event);
  340.  
  341.                                         const float distSq = Distance::Point_PointSq(vAIActorPos, vProbTargetPos);
  342.                                         pAIActor->CheckCloseContact(pProbTarget, distSq);
  343.                                 }
  344.                         }
  345.                 }
  346.         }
  347.  
  348.         return false;
  349. }
  350.  
  351. bool CPerceptionManager::FilterStimulus(SAIStimulus* stim)
  352. {
  353.         const SAIStimulusTypeDesc* desc = &m_stimulusTypes[stim->type];
  354.         const SAIStimulusFilter* filters[AI_MAX_FILTERS];
  355.  
  356.         // Merge and filter with current active stimuli.
  357.         for (unsigned int i = 0; i < AI_MAX_STIMULI; ++i)
  358.         {
  359.                 unsigned int mask = (1 << i);
  360.                 if ((mask & desc->filterTypes) == 0) continue;
  361.  
  362.                 // Collect filters for this stimuli type
  363.                 unsigned int nFilters = 0;
  364.                 for (unsigned j = 0; j < desc->nFilters; ++j)
  365.                 {
  366.                         const SAIStimulusFilter* filter = &desc->filters[j];
  367.                         if ((unsigned int)filter->type != stim->type) continue;
  368.                         if (filter->subType && (unsigned int)filter->subType != stim->subType) continue;
  369.                         filters[nFilters++] = filter;
  370.                 }
  371.                 if (nFilters == 0)
  372.                         continue;
  373.  
  374.                 std::vector<SStimulusRecord>& stimuli = m_stimuli[i];
  375.                 for (unsigned j = 0, nj = stimuli.size(); j < nj; ++j)
  376.                 {
  377.                         SStimulusRecord& s = stimuli[j];
  378.                         Vec3 diff = stim->pos - s.pos;
  379.                         float distSq = diff.GetLengthSquared();
  380.  
  381.                         // Apply filters
  382.                         for (unsigned int k = 0; k < nFilters; ++k)
  383.                         {
  384.                                 const SAIStimulusFilter* filter = filters[k];
  385.                                 if (filter->subType && (filter->subType & (1 << s.subType))) continue;
  386.  
  387.                                 if (filter->merge == AISTIMFILTER_MERGE_AND_DISCARD)
  388.                                 {
  389.                                         // Merge stimuli
  390.                                         // Allow to merge stimuli before they are processed.
  391.                                         const float duration = desc->duration[stim->subType];
  392.                                         if (distSq < sqr(s.radius * filter->scale))
  393.                                         {
  394.                                                 if ((duration - s.t) < desc->processDelay)
  395.                                                 {
  396.                                                         float dist = sqrtf(distSq);
  397.                                                         // Merge spheres into a larger one.
  398.                                                         if (dist > 0.00001f)
  399.                                                         {
  400.                                                                 diff *= 1.0f / dist;
  401.                                                                 // Calc new radius
  402.                                                                 float r = s.radius;
  403.                                                                 s.radius = (dist + r + stim->radius) / 2;
  404.                                                                 // Calc new location
  405.                                                                 s.pos += diff * (s.radius - r);
  406.                                                         }
  407.                                                         else
  408.                                                         {
  409.                                                                 // Spheres are at same location, merge radii.
  410.                                                                 s.radius = max(s.radius, stim->radius);
  411.                                                         }
  412.                                                 }
  413.                                                 return true;
  414.                                         }
  415.                                 }
  416.                                 else
  417.                                 {
  418.                                         // Discard stimuli
  419.                                         if (distSq < sqr(s.radius * filter->scale))
  420.                                                 return true;
  421.                                 }
  422.                         }
  423.                 }
  424.  
  425.         }
  426.  
  427.         // Not filtered nor merged, should create new stimulus.
  428.         return false;
  429. }
  430.  
  431. //===================================================================
  432. // Update
  433. //===================================================================
  434. void CPerceptionManager::Update(float dt)
  435. {
  436.  
  437.         // Update stats about the incoming stimulus.
  438.         m_stats.trackers[PERFTRACK_INCOMING_STIMS].Inc(m_incomingStimuli.size());
  439.  
  440.         // Process incoming stimuli
  441.         bool previousDiscarded = false;
  442.         for (unsigned i = 0, ni = m_incomingStimuli.size(); i < ni; ++i)
  443.         {
  444.                 SAIStimulus& is = m_incomingStimuli[i];
  445.                 // Check if stimulus should be discarded because it is set linked to the discard rules
  446.                 // of the previous stimulus.
  447.                 bool discardWithPrevious = previousDiscarded && (is.flags & AISTIMPROC_FILTER_LINK_WITH_PREVIOUS);
  448.                 if (!discardWithPrevious && !FilterStimulus(&is))
  449.                 {
  450.                         const SAIStimulusTypeDesc* desc = &m_stimulusTypes[is.type];
  451.                         std::vector<SStimulusRecord>& stimuli = m_stimuli[is.type];
  452.  
  453.                         // Create new Stimulus
  454.                         stimuli.resize(stimuli.size() + 1);
  455.                         SStimulusRecord& stim = stimuli.back();
  456.                         stim.sourceId = is.sourceId;
  457.                         stim.targetId = is.targetId;
  458.                         stim.pos = is.pos;
  459.                         stim.dir = is.dir;
  460.                         stim.radius = is.radius;
  461.                         stim.t = desc->duration[is.subType];
  462.                         stim.type = is.type;
  463.                         stim.subType = is.subType;
  464.                         stim.flags = is.flags;
  465.                         stim.dispatched = 0;
  466.  
  467.                         previousDiscarded = false;
  468.                 }
  469.                 else
  470.                 {
  471.                         previousDiscarded = true;
  472.                 }
  473.         }
  474.         m_incomingStimuli.clear();
  475.  
  476.         // Update stimuli
  477.         // Merge and filter with current active stimuli.
  478.         for (unsigned int i = 0; i < AI_MAX_STIMULI; ++i)
  479.         {
  480.                 std::vector<SStimulusRecord>& stims = m_stimuli[i];
  481.                 for (unsigned j = 0; j < stims.size(); )
  482.                 {
  483.                         SStimulusRecord& stim = stims[j];
  484.                         const SAIStimulusTypeDesc* desc = &m_stimulusTypes[stim.type];
  485.  
  486.                         // Dispatch
  487.                         if (!stim.dispatched && stim.t < (desc->duration[stim.subType] - desc->processDelay))
  488.                         {
  489.                                 float threat = 1.0f;
  490.                                 switch (stim.type)
  491.                                 {
  492.                                 case AISTIM_SOUND:
  493.                                         HandleSound(stim);
  494.                                         switch (stim.subType)
  495.                                         {
  496.                                         case AISOUND_GENERIC:
  497.                                                 threat = 0.3f;
  498.                                                 break;
  499.                                         case AISOUND_COLLISION:
  500.                                                 threat = 0.3f;
  501.                                                 break;
  502.                                         case AISOUND_COLLISION_LOUD:
  503.                                                 threat = 0.3f;
  504.                                                 break;
  505.                                         case AISOUND_MOVEMENT:
  506.                                                 threat = 0.5f;
  507.                                                 break;
  508.                                         case AISOUND_MOVEMENT_LOUD:
  509.                                                 threat = 0.5f;
  510.                                                 break;
  511.                                         case AISOUND_WEAPON:
  512.                                                 threat = 1.0f;
  513.                                                 break;
  514.                                         case AISOUND_EXPLOSION:
  515.                                                 threat = 1.0f;
  516.                                                 break;
  517.                                         }
  518.                                         ;
  519.                                         break;
  520.                                 case AISTIM_COLLISION:
  521.                                         HandleCollision(stim);
  522.                                         threat = 0.2f;
  523.                                         break;
  524.                                 case AISTIM_EXPLOSION:
  525.                                         HandleExplosion(stim);
  526.                                         threat = 1.0f;
  527.                                         break;
  528.                                 case AISTIM_BULLET_WHIZZ:
  529.                                         HandleBulletWhizz(stim);
  530.                                         threat = 0.7f;
  531.                                         break;
  532.                                 case AISTIM_BULLET_HIT:
  533.                                         HandleBulletHit(stim);
  534.                                         threat = 1.0f;
  535.                                         break;
  536.                                 case AISTIM_GRENADE:
  537.                                         HandleGrenade(stim);
  538.                                         threat = 0.7f;
  539.                                         break;
  540.                                 default:
  541.                                         // Invalid type
  542.                                         AIAssert(0);
  543.                                         break;
  544.                                 }
  545.  
  546.                                 NotifyAIEventListeners(stim, threat);
  547.  
  548.                                 stim.dispatched = 1;
  549.                         }
  550.  
  551.                         // Update stimulus time.
  552.                         stim.t -= dt;
  553.                         if (stim.t < 0.0f)
  554.                         {
  555.                                 // The stimuli has timed out, delete it.
  556.                                 if (&stims[j] != &stims.back())
  557.                                         stims[j] = stims.back();
  558.                                 stims.pop_back();
  559.                         }
  560.                         else
  561.                         {
  562.                                 // Advance
  563.                                 ++j;
  564.                         }
  565.                 }
  566.  
  567.                 // Update stats about the stimulus.
  568.                 m_stats.trackers[PERFTRACK_STIMS].Inc(stims.size());
  569.  
  570.                 // Update the ignores.
  571.                 if (!m_ignoreStimuliFrom[i].empty())
  572.                 {
  573.                         StimulusIgnoreMap::iterator it = m_ignoreStimuliFrom[i].begin();
  574.                         while (it != m_ignoreStimuliFrom[i].end())
  575.                         {
  576.                                 it->second -= dt;
  577.                                 if (it->second <= 0.0f)
  578.                                 {
  579.                                         StimulusIgnoreMap::iterator del = it;
  580.                                         ++it;
  581.                                         m_ignoreStimuliFrom[i].erase(del);
  582.                                 }
  583.                                 else
  584.                                         ++it;
  585.                         }
  586.                 }
  587.         }
  588.  
  589.         VisCheckBroadPhase(dt);
  590.  
  591.         // Update stats
  592.         m_stats.Update();
  593. }
  594.  
  595. //===================================================================
  596. // VisCheckBroadPhase
  597. //===================================================================
  598. void CPerceptionManager::VisCheckBroadPhase(float dt)
  599. {
  600.         FUNCTION_PROFILER(gEnv->pSystem, PROFILE_AI);
  601.  
  602.         ActorLookUp& lookUp = *gAIEnv.pActorLookUp;
  603.         lookUp.Prepare(ActorLookUp::Position | ActorLookUp::EntityID);
  604.  
  605.         const float UPDATE_DELTA_TIME = 0.2f;
  606.  
  607.         m_visBroadPhaseDt += dt;
  608.         if (m_visBroadPhaseDt < UPDATE_DELTA_TIME)
  609.                 return;
  610.         m_visBroadPhaseDt -= UPDATE_DELTA_TIME;
  611.         if (m_visBroadPhaseDt > UPDATE_DELTA_TIME) m_visBroadPhaseDt = 0.0f;
  612.  
  613.         size_t activeActorCount = lookUp.GetActiveCount();
  614.  
  615.         if (!activeActorCount)
  616.                 return;
  617.  
  618.         // Find player vehicles (driver inside but disabled).
  619.         s_playerVehicles.clear();
  620.  
  621.         const AIObjectOwners::iterator vehicleBegin = gAIEnv.pAIObjectManager->m_Objects.find(AIOBJECT_VEHICLE);
  622.         const AIObjectOwners::iterator aiEnd = gAIEnv.pAIObjectManager->m_Objects.end();
  623.         for (AIObjectOwners::iterator ai = vehicleBegin; ai != aiEnd && ai->first == AIOBJECT_VEHICLE; ++ai)
  624.         {
  625.                 CAIVehicle* pVehicle = (CAIVehicle*)ai->second.GetAIObject();
  626.                 if (!pVehicle->IsEnabled() && pVehicle->IsDriverInside())
  627.                         s_playerVehicles.push_back(pVehicle);
  628.         }
  629.  
  630.         // Find target entities
  631.         s_targetEntities.clear();
  632.  
  633.         const AIObjectOwners::iterator targetsIt = gAIEnv.pAIObjectManager->m_Objects.find(AIOBJECT_TARGET);
  634.         const AIObjectOwners::iterator targetsEnd = gAIEnv.pAIObjectManager->m_Objects.end();
  635.  
  636.         for (AIObjectOwners::iterator ai = targetsIt; ai != targetsEnd && ai->first == AIOBJECT_TARGET; ++ai)
  637.         {
  638.                 CAIObject* pTarget = ai->second.GetAIObject();
  639.                 if (pTarget->IsEnabled())
  640.                         s_targetEntities.push_back(pTarget);
  641.         }
  642.  
  643.         // Clear potential targets.
  644.         for (size_t actorIndex = 0; actorIndex < activeActorCount; ++actorIndex)
  645.         {
  646.                 CAIActor* pAIActor = lookUp.GetActor<CAIActor>(actorIndex);
  647.                 pAIActor->ClearProbableTargets();
  648.         }
  649.  
  650.         // Find potential targets.
  651.         for (size_t actorIndex = 0; actorIndex < activeActorCount; ++actorIndex)
  652.         {
  653.                 CAIActor* pFirst = lookUp.GetActor<CAIActor>(actorIndex);
  654.  
  655.                 const Vec3 firstPos = lookUp.GetPosition(actorIndex);
  656.  
  657.                 // Check against other AIs
  658.                 for (size_t actorIndex2 = actorIndex + 1; actorIndex2 < activeActorCount; ++actorIndex2)
  659.                 {
  660.                         CAIActor* pSecond = lookUp.GetActor<CAIActor>(actorIndex2);
  661.  
  662.                         const Vec3 secondPos = lookUp.GetPosition(actorIndex2);
  663.  
  664.                         float distSq = Distance::Point_PointSq(firstPos, secondPos);
  665.                         if ((distSq < sqr(pFirst->GetMaxTargetVisibleRange(pSecond))) && pFirst->IsHostile(pSecond))
  666.                                 pFirst->AddProbableTarget(pSecond);
  667.  
  668.                         if ((distSq < sqr(pSecond->GetMaxTargetVisibleRange(pFirst)) && pSecond->IsHostile(pFirst)))
  669.                                 pSecond->AddProbableTarget(pFirst);
  670.                 }
  671.  
  672.                 // Check against player vehicles.
  673.                 for (unsigned j = 0, nj = s_playerVehicles.size(); j < nj; ++j)
  674.                 {
  675.                         CAIVehicle* pSecond = s_playerVehicles[j];
  676.  
  677.                         const Vec3 secondPos = pSecond->GetPos();
  678.  
  679.                         float distSq = Distance::Point_PointSq(firstPos, secondPos);
  680.                         if ((distSq < sqr(pFirst->GetMaxTargetVisibleRange(pSecond))) && pFirst->IsHostile(pSecond))
  681.                                 pFirst->AddProbableTarget(pSecond);
  682.                 }
  683.  
  684.                 // Check against target entities.
  685.                 for (unsigned j = 0, nj = s_targetEntities.size(); j < nj; ++j)
  686.                 {
  687.                         CAIObject* pSecond = s_targetEntities[j];
  688.  
  689.                         const Vec3 secondPos = pSecond->GetPos();
  690.  
  691.                         float distSq = Distance::Point_PointSq(firstPos, secondPos);
  692.                         if ((distSq < sqr(pFirst->GetMaxTargetVisibleRange(pSecond))) && pFirst->IsHostile(pSecond))
  693.                                 pFirst->AddProbableTarget(pSecond);
  694.                 }
  695.         }
  696. }
  697.  
  698. inline int bit(unsigned b, unsigned x)
  699. {
  700.         return (x >> b) & 1;
  701. }
  702.  
  703. inline ColorB GetColorFromId(unsigned x)
  704. {
  705.         unsigned r = (bit(0, x) << 7) | (bit(4, x) << 5) | (bit(7, x) << 3);
  706.         unsigned g = (bit(1, x) << 7) | (bit(5, x) << 5) | (bit(8, x) << 3);
  707.         unsigned b = (bit(2, x) << 7) | (bit(6, x) << 5) | (bit(9, x) << 3);
  708.         return ColorB(255 - r, 255 - g, 255 - b);
  709. }
  710.  
  711. //===================================================================
  712. // DebugDraw
  713. //===================================================================
  714. void CPerceptionManager::DebugDraw(int mode)
  715. {
  716.         CAIPlayer* pPlayer = CastToCAIPlayerSafe(GetAISystem()->GetPlayer());
  717.  
  718.         typedef std::map<unsigned, unsigned> OccMap;
  719.         OccMap occupied;
  720.  
  721.         CDebugDrawContext dc;
  722.  
  723.         for (unsigned i = 0; i < AI_MAX_STIMULI; ++i)
  724.         {
  725.                 switch (i)
  726.                 {
  727.                 case AISTIM_SOUND:
  728.                         if (gAIEnv.CVars.DrawSoundEvents == 0) continue;
  729.                         break;
  730.                 case AISTIM_COLLISION:
  731.                         if (gAIEnv.CVars.DrawCollisionEvents == 0) continue;
  732.                         break;
  733.                 case AISTIM_EXPLOSION:
  734.                         if (gAIEnv.CVars.DrawExplosions == 0) continue;
  735.                         break;
  736.                 case AISTIM_BULLET_WHIZZ:
  737.                 case AISTIM_BULLET_HIT:
  738.                         if (gAIEnv.CVars.DrawBulletEvents == 0) continue;
  739.                         break;
  740.                 case AISTIM_GRENADE:
  741.                         if (gAIEnv.CVars.DrawGrenadeEvents == 0) continue;
  742.                         break;
  743.                 }
  744.                 ;
  745.  
  746.                 const SAIStimulusTypeDesc* desc = &m_stimulusTypes[i];
  747.                 std::vector<SStimulusRecord>& stimuli = m_stimuli[i];
  748.                 for (unsigned j = 0, nj = stimuli.size(); j < nj; ++j)
  749.                 {
  750.                         SStimulusRecord& s = stimuli[j];
  751.  
  752.                         assert(s.subType < AI_MAX_SUBTYPES);
  753.                         float tmax = desc->duration[s.subType];
  754.                         float a = clamp_tpl((s.t - tmax / 2) / (tmax / 2), 0.0f, 1.0f);
  755.  
  756.                         int row = 0;
  757.                         unsigned hash = HashFromVec3(s.pos, 0.1f, 1.0f / 0.1f);
  758.                         OccMap::iterator it = occupied.find(hash);
  759.                         if (it != occupied.end())
  760.                         {
  761.                                 row = it->second;
  762.                                 it->second++;
  763.                         }
  764.                         else
  765.                         {
  766.                                 occupied[hash] = 1;
  767.                         }
  768.                         if (row > 5) row = 5;
  769.  
  770.                         bool thrownByPlayer = pPlayer ? pPlayer->IsThrownByPlayer(s.sourceId) : false;
  771.  
  772.                         ColorB color = thrownByPlayer ? ColorB(240, 16, 0) : GetColorFromId(i);
  773.                         ColorB colorTrans(color);
  774.                         color.a = 10 + (uint8)(240 * a);
  775.                         colorTrans.a = 10 + (uint8)(128 * a);
  776.  
  777.                         char rowTxt[] = "\n\n\n\n\n\n\n";
  778.                         rowTxt[row] = '\0';
  779.  
  780.                         char subTypeTxt[128] = "";
  781.                         switch (s.type)
  782.                         {
  783.                         case AISTIM_SOUND:
  784.                                 switch (s.subType)
  785.                                 {
  786.                                 case AISOUND_GENERIC:
  787.                                         cry_sprintf(subTypeTxt, "GENERIC  R=%.1f", s.radius);
  788.                                         break;
  789.                                 case AISOUND_COLLISION:
  790.                                         cry_sprintf(subTypeTxt, "COLLISION  R=%.1f", s.radius);
  791.                                         break;
  792.                                 case AISOUND_COLLISION_LOUD:
  793.                                         cry_sprintf(subTypeTxt, "COLLISION LOUD  R=%.1f", s.radius);
  794.                                         break;
  795.                                 case AISOUND_MOVEMENT:
  796.                                         cry_sprintf(subTypeTxt, "MOVEMENT  R=%.1f", s.radius);
  797.                                         break;
  798.                                 case AISOUND_MOVEMENT_LOUD:
  799.                                         cry_sprintf(subTypeTxt, "MOVEMENT LOUD  R=%.1f", s.radius);
  800.                                         break;
  801.                                 case AISOUND_WEAPON:
  802.                                         cry_sprintf(subTypeTxt, "WEAPON\nR=%.1f", s.radius);
  803.                                         break;
  804.                                 case AISOUND_EXPLOSION:
  805.                                         cry_sprintf(subTypeTxt, "EXPLOSION  R=%.1f", s.radius);
  806.                                         break;
  807.                                 }
  808.                                 break;
  809.                         case AISTIM_COLLISION:
  810.                                 switch (s.subType)
  811.                                 {
  812.                                 case AICOL_SMALL:
  813.                                         cry_sprintf(subTypeTxt, "SMALL  R=%.1f", s.radius);
  814.                                         break;
  815.                                 case AICOL_MEDIUM:
  816.                                         cry_sprintf(subTypeTxt, "MEDIUM  R=%.1f", s.radius);
  817.                                         break;
  818.                                 case AICOL_LARGE:
  819.                                         cry_sprintf(subTypeTxt, "LARGE  R=%.1f", s.radius);
  820.                                         break;
  821.                                 }
  822.                                 ;
  823.                                 break;
  824.                         case AISTIM_EXPLOSION:
  825.                         case AISTIM_BULLET_WHIZZ:
  826.                         case AISTIM_BULLET_HIT:
  827.                         case AISTIM_GRENADE:
  828.                                 cry_sprintf(subTypeTxt, "R=%.1f", s.radius);
  829.                                 break;
  830.                         default:
  831.                                 break;
  832.                         }
  833.  
  834.                         Vec3 ext(0, 0, s.radius);
  835.                         if (s.dir.GetLengthSquared() > 0.1f)
  836.                                 ext = s.dir * s.radius;
  837.  
  838.                         dc->DrawSphere(s.pos, 0.15f, colorTrans);
  839.                         dc->DrawLine(s.pos, color, s.pos + ext, color);
  840.                         dc->DrawWireSphere(s.pos, s.radius, color);
  841.                         dc->Draw3dLabel(s.pos, 1.1f, "%s%s  %s", rowTxt, desc->name, subTypeTxt);
  842.                 }
  843.         }
  844.  
  845. }
  846.  
  847. //===================================================================
  848. // DebugDrawPerformance
  849. //===================================================================
  850. void CPerceptionManager::DebugDrawPerformance(int mode)
  851. {
  852.         CDebugDrawContext dc;
  853.         ColorB white(255, 255, 255);
  854.  
  855.         static std::vector<Vec3> points;
  856.  
  857.         if (mode == 1)
  858.         {
  859.                 // Draw visibility performance
  860.  
  861.                 const int visChecks = m_stats.trackers[PERFTRACK_VIS_CHECKS].GetCount();
  862.                 const int visChecksMax = m_stats.trackers[PERFTRACK_VIS_CHECKS].GetCountMax();
  863.                 const int updates = m_stats.trackers[PERFTRACK_UPDATES].GetCount();
  864.                 const int updatesMax = m_stats.trackers[PERFTRACK_UPDATES].GetCountMax();
  865.  
  866.                 dc->Draw2dLabel(50, 200, 2, white, false, "Updates:");
  867.                 dc->Draw2dLabel(175, 200, 2, white, false, "%d", updates);
  868.                 dc->Draw2dLabel(215, 200 + 5, 1, white, false, "max:%d", updatesMax);
  869.  
  870.                 dc->Draw2dLabel(50, 225, 2, white, false, "Vis checks:");
  871.                 dc->Draw2dLabel(175, 225, 2, white, false, "%d", visChecks);
  872.                 dc->Draw2dLabel(215, 225 + 5, 1, white, false, "max:%d", visChecksMax);
  873.  
  874.                 {
  875.                         dc->Init2DMode();
  876.                         dc->SetAlphaBlended(true);
  877.                         dc->SetBackFaceCulling(false);
  878.                         dc->SetDepthTest(false);
  879.  
  880.                         float rw = (float) dc->GetWidth();
  881.                         float rh = (float) dc->GetHeight();
  882.                         //              float   as = rh/rw;
  883.                         float scale = 1.0f / rh;
  884.  
  885.                         // Divider lines every 10 units.
  886.                         for (unsigned i = 0; i <= 10; ++i)
  887.                         {
  888.                                 int v = i * 10;
  889.                                 dc->DrawLine(Vec3(0.1f, 0.9f - v * scale, 0), ColorB(255, 255, 255, 128), Vec3(0.9f, 0.9f - v * scale, 0), ColorB(255, 255, 255, 128));
  890.                                 dc->Draw2dLabel(0.1f * rw - 20, rh * 0.9f - v * scale * rh - 6, 1.0f, white, false, "%d", i * 10);
  891.                         }
  892.  
  893.                         int idx[2] = { PERFTRACK_UPDATES, PERFTRACK_VIS_CHECKS };
  894.                         ColorB colors[3] = { ColorB(255, 0, 0), ColorB(0, 196, 255), ColorB(255, 255, 255) };
  895.  
  896.                         for (int i = 0; i < 2; ++i)
  897.                         {
  898.                                 const CValueHistory<int>& hist = m_stats.trackers[idx[i]].GetHist();
  899.                                 unsigned n = hist.GetSampleCount();
  900.                                 if (!n) continue;
  901.  
  902.                                 points.resize(n);
  903.  
  904.                                 for (unsigned j = 0; j < n; ++j)
  905.                                 {
  906.                                         float t = (float)j / (float)hist.GetMaxSampleCount();
  907.                                         points[j].x = 0.1f + t * 0.8f;
  908.                                         points[j].y = 0.9f - hist.GetSample(j) * scale;
  909.                                         points[j].z = 0;
  910.                                 }
  911.                                 dc->DrawPolyline(&points[0], n, false, colors[i]);
  912.                         }
  913.                 }
  914.  
  915.                 ActorLookUp& lookUp = *gAIEnv.pActorLookUp;
  916.                 size_t activeActorCount = lookUp.GetActiveCount();
  917.  
  918.                 for (size_t actorIndex = 0; actorIndex < activeActorCount; ++actorIndex)
  919.                 {
  920.                         CAIActor* pAIActor = lookUp.GetActor<CAIActor>(actorIndex);
  921.                         dc->DrawSphere(lookUp.GetPosition(actorIndex), 1.0f, ColorB(255, 0, 0));
  922.                 }
  923.         }
  924.         else if (mode == 2)
  925.         {
  926.                 // Draw stims performance
  927.  
  928.                 const int incoming = m_stats.trackers[PERFTRACK_INCOMING_STIMS].GetCount();
  929.                 const int incomingMax = m_stats.trackers[PERFTRACK_INCOMING_STIMS].GetCountMax();
  930.                 const int stims = m_stats.trackers[PERFTRACK_STIMS].GetCount();
  931.                 const int stimsMax = m_stats.trackers[PERFTRACK_STIMS].GetCountMax();
  932.  
  933.                 dc->Draw2dLabel(50, 200, 2, white, false, "Incoming:");
  934.                 dc->Draw2dLabel(175, 200, 2, white, false, "%d", incoming);
  935.                 dc->Draw2dLabel(215, 200 + 5, 1, white, false, "max:%d", incomingMax);
  936.  
  937.                 dc->Draw2dLabel(50, 225, 2, white, false, "Stims:");
  938.                 dc->Draw2dLabel(175, 225, 2, white, false, "%d", stims);
  939.                 dc->Draw2dLabel(215, 225 + 5, 1, white, false, "max:%d", stimsMax);
  940.  
  941.                 dc->Init2DMode();
  942.                 dc->SetAlphaBlended(true);
  943.                 dc->SetBackFaceCulling(false);
  944.                 dc->SetDepthTest(false);
  945.  
  946.                 float rw = (float) dc->GetWidth();
  947.                 float rh = (float) dc->GetHeight();
  948.                 //              float   as = rh/rw;
  949.                 float scale = 1.0f / rh;
  950.  
  951.                 // Divider lines every 10 units.
  952.                 for (unsigned i = 0; i <= 10; ++i)
  953.                 {
  954.                         int v = i * 10;
  955.                         dc->DrawLine(Vec3(0.1f, 0.9f - v * scale, 0), ColorB(255, 255, 255, 128), Vec3(0.9f, 0.9f - v * scale, 0), ColorB(255, 255, 255, 128));
  956.                         dc->Draw2dLabel(0.1f * rw - 20, rh * 0.9f - v * scale * rh - 6, 1.0f, white, false, "%d", i * 10);
  957.                 }
  958.  
  959.                 int idx[2] = { PERFTRACK_INCOMING_STIMS, PERFTRACK_STIMS };
  960.                 ColorB colors[2] = { ColorB(0, 196, 255), ColorB(255, 255, 255) };
  961.  
  962.                 for (int i = 0; i < 2; ++i)
  963.                 {
  964.                         const CValueHistory<int>& hist = m_stats.trackers[idx[i]].GetHist();
  965.                         unsigned n = hist.GetSampleCount();
  966.                         if (!n) continue;
  967.  
  968.                         points.resize(n);
  969.  
  970.                         for (unsigned j = 0; j < n; ++j)
  971.                         {
  972.                                 float t = (float)j / (float)hist.GetMaxSampleCount();
  973.                                 points[j].x = 0.1f + t * 0.8f;
  974.                                 points[j].y = 0.9f - hist.GetSample(j) * scale;
  975.                                 points[j].z = 0;
  976.                         }
  977.                         dc->DrawPolyline(&points[0], n, false, colors[i]);
  978.                 }
  979.         }
  980. }
  981.  
  982. void CPerceptionManager::RegisterStimulus(const SAIStimulus& stim)
  983. {
  984.         // Check if we should ignore stimulus from the source.
  985.         StimulusIgnoreMap& ignore = m_ignoreStimuliFrom[stim.type];
  986.         if (ignore.find(stim.sourceId) != ignore.end())
  987.                 return;
  988.  
  989.         //      const SAIStimulusTypeDesc* desc = &m_stimulusTypes[stim.type];
  990.         m_incomingStimuli.resize(m_incomingStimuli.size() + 1);
  991.         SAIStimulus& is = m_incomingStimuli.back();
  992.         is = stim;
  993.  
  994.         //      m_maxIncomingPerUpdate = max(m_maxIncomingPerUpdate, (int)m_incomingStimuli.size());
  995. }
  996.  
  997. //===================================================================
  998. // IgnoreStimulusFrom
  999. //===================================================================
  1000. void CPerceptionManager::IgnoreStimulusFrom(EntityId sourceId, EAIStimulusType type, float time)
  1001. {
  1002.         StimulusIgnoreMap& ignore = m_ignoreStimuliFrom[(int)type];
  1003.         StimulusIgnoreMap::iterator it = ignore.find(sourceId);
  1004.         if (it != ignore.end())
  1005.                 it->second = max(it->second, time);
  1006.         else
  1007.                 ignore[sourceId] = time;
  1008. }
  1009.  
  1010. //===================================================================
  1011. // HandleSound
  1012. //===================================================================
  1013. void CPerceptionManager::HandleSound(const SStimulusRecord& stim)
  1014. {
  1015.         FUNCTION_PROFILER(gEnv->pSystem, PROFILE_AI);
  1016.  
  1017.         ActorLookUp& lookUp = *gAIEnv.pActorLookUp;
  1018.         lookUp.Prepare(ActorLookUp::EntityID);
  1019.  
  1020.         IEntity* pSourceEntity = gEnv->pEntitySystem->GetEntity(stim.sourceId);
  1021.         IEntity* pTargetEntity = gEnv->pEntitySystem->GetEntity(stim.targetId);
  1022.  
  1023.         CAIActor* pSourceAI = pSourceEntity ? CastToCAIActorSafe(pSourceEntity->GetAI()) : NULL;
  1024.  
  1025.         // If this is a collision sound
  1026.         if (stim.type == AISOUND_COLLISION || stim.type == AISOUND_COLLISION_LOUD)
  1027.         {
  1028.                 CAIActor* pTargetAI = pTargetEntity ? CastToCAIActorSafe(pTargetEntity->GetAI()) : NULL;
  1029.  
  1030.                 // Do not report collision sounds between two AI
  1031.                 if (pSourceAI || pTargetAI)
  1032.                         return;
  1033.         }
  1034.  
  1035.         // perform suppression of the sound from any nearby suppressors
  1036.         float rad = SupressSound(stim.pos, stim.radius);
  1037.  
  1038.         EntityId throwingEntityID = 0;
  1039.  
  1040.         // If the sound events comes from an object thrown by the player
  1041.         if (CAIPlayer* pPlayer = CastToCAIPlayerSafe(GetAISystem()->GetPlayer()))
  1042.         {
  1043.                 if (pPlayer->IsThrownByPlayer(stim.sourceId))
  1044.                 {
  1045.                         // Record the thrower (to use as targetID - required for target tracking)
  1046.                         throwingEntityID = pPlayer->GetEntityID();
  1047.                 }
  1048.         }
  1049.  
  1050.         size_t activeCount = lookUp.GetActiveCount();
  1051.  
  1052.         for (size_t actorIndex = 0; actorIndex < activeCount; ++actorIndex)
  1053.         {
  1054.                 CAIActor* pReceiverAIActor = lookUp.GetActor<CAIActor>(actorIndex);
  1055.  
  1056.                 // Do not report sounds to the sound source.
  1057.                 if (lookUp.GetEntityID(actorIndex) == stim.sourceId)
  1058.                         continue;
  1059.  
  1060.                 if (!pReceiverAIActor->IsPerceptionEnabled())
  1061.                         continue;
  1062.  
  1063.                 if (pSourceAI)
  1064.                 {
  1065.                         const bool isHostile = pSourceAI->IsHostile(pReceiverAIActor);
  1066.  
  1067.                         // Ignore impact or explosion sounds from the same species and group
  1068.                         switch (stim.subType)
  1069.                         {
  1070.                         case AISOUND_COLLISION:
  1071.                         case AISOUND_COLLISION_LOUD:
  1072.                         case AISOUND_EXPLOSION:
  1073.  
  1074.                                 if (!isHostile && pReceiverAIActor->GetGroupId() == pSourceAI->GetGroupId())
  1075.                                         continue;
  1076.                         }
  1077.  
  1078.                         // No vehicle sounds for same species - destructs convoys
  1079.                         if (!isHostile && pSourceAI->GetType() == AIOBJECT_VEHICLE)
  1080.                                 continue;
  1081.                 }
  1082.  
  1083.                 // The sound is occluded because of the buildings.
  1084.                 if (IsSoundOccluded(pReceiverAIActor, stim.pos))
  1085.                         continue;
  1086.  
  1087.                 // Send event.
  1088.                 SAIEVENT event;
  1089.                 event.vPosition = stim.pos;
  1090.                 event.fThreat = rad;
  1091.                 event.nType = (int)stim.subType;
  1092.                 event.nFlags = stim.flags;
  1093.                 event.sourceId = throwingEntityID ? throwingEntityID : (pSourceAI ? pSourceAI->GetPerceivedEntityID() : stim.sourceId);
  1094.                 pReceiverAIActor->Event(AIEVENT_ONSOUNDEVENT, &event);
  1095.  
  1096.                 RecordStimulusEvent(stim, event.fThreat, *pReceiverAIActor);
  1097.         }
  1098. }
  1099.  
  1100. //===================================================================
  1101. // HandleCollisionEvent
  1102. //===================================================================
  1103. void CPerceptionManager::HandleCollision(const SStimulusRecord& stim)
  1104. {
  1105.         FUNCTION_PROFILER(gEnv->pSystem, PROFILE_AI);
  1106.  
  1107.         ActorLookUp& lookUp = *gAIEnv.pActorLookUp;
  1108.         lookUp.Prepare(ActorLookUp::Position);
  1109.  
  1110.         IEntity* pCollider = gEnv->pEntitySystem->GetEntity(stim.sourceId);
  1111.         IEntity* pTarget = gEnv->pEntitySystem->GetEntity(stim.targetId);
  1112.  
  1113.         CAIActor* pColliderAI = pCollider ? CastToCAIActorSafe(pCollider->GetAI()) : 0;
  1114.         CAIActor* pTargetAI = pTarget ? CastToCAIActorSafe(pTarget->GetAI()) : 0;
  1115.  
  1116.         // Do not report AI collisions
  1117.         if (pColliderAI || pTargetAI)
  1118.                 return;
  1119.  
  1120.         bool thrownByPlayer = false;
  1121.         if (pCollider)
  1122.         {
  1123.                 CAIPlayer* pPlayer = CastToCAIPlayerSafe(GetAISystem()->GetPlayer());
  1124.                 if (pPlayer)
  1125.                 {
  1126.                         thrownByPlayer = pPlayer->IsThrownByPlayer(pCollider->GetId());
  1127.                 }
  1128.         }
  1129.  
  1130.         // Allow to react to larger objects which collide nearby.
  1131.         size_t activeCount = lookUp.GetActiveCount();
  1132.  
  1133.         for (size_t actorIndex = 0; actorIndex < activeCount; ++actorIndex)
  1134.         {
  1135.                 CAIActor* pReceiverAIActor = lookUp.GetActor<CAIActor>(actorIndex);
  1136.  
  1137.                 const float scale = pReceiverAIActor->GetParameters().m_PerceptionParams.collisionReactionScale;
  1138.                 const float rangeSq = sqr(stim.radius * scale);
  1139.  
  1140.                 float distSq = Distance::Point_PointSq(stim.pos, lookUp.GetPosition(actorIndex));
  1141.                 if (distSq > rangeSq)
  1142.                         continue;
  1143.  
  1144.                 if (!pReceiverAIActor->IsPerceptionEnabled())
  1145.                         continue;
  1146.  
  1147.                 IAISignalExtraData* pData = GetAISystem()->CreateSignalExtraData();
  1148.                 pData->iValue = thrownByPlayer ? 1 : 0;
  1149.                 pData->fValue = sqrtf(distSq);
  1150.                 pData->point = stim.pos;
  1151.                 pReceiverAIActor->SetSignal(0, "OnCloseCollision", 0, pData);
  1152.  
  1153.                 if (thrownByPlayer)
  1154.                         if (CPuppet* pReceiverPuppet = pReceiverAIActor->CastToCPuppet())
  1155.                                 pReceiverPuppet->SetAlarmed();
  1156.  
  1157.                 RecordStimulusEvent(stim, 0.0f, *pReceiverAIActor);
  1158.         }
  1159. }
  1160.  
  1161. //===================================================================
  1162. // HandleExplosion
  1163. //===================================================================
  1164. void CPerceptionManager::HandleExplosion(const SStimulusRecord& stim)
  1165. {
  1166.         FUNCTION_PROFILER(gEnv->pSystem, PROFILE_AI);
  1167.  
  1168.         ActorLookUp& lookUp = *gAIEnv.pActorLookUp;
  1169.         lookUp.Prepare(ActorLookUp::Position);
  1170.  
  1171.         // React to explosions.
  1172.         size_t activeCount = lookUp.GetActiveCount();
  1173.  
  1174.         for (size_t actorIndex = 0; actorIndex < activeCount; ++actorIndex)
  1175.         {
  1176.                 CAIActor* pReceiverAIActor = lookUp.GetActor<CAIActor>(actorIndex);
  1177.  
  1178.                 const float scale = pReceiverAIActor->GetParameters().m_PerceptionParams.collisionReactionScale;
  1179.                 const float rangeSq = sqr(stim.radius * scale);
  1180.  
  1181.                 float distSq = Distance::Point_PointSq(stim.pos, lookUp.GetPosition(actorIndex));
  1182.                 if (distSq > rangeSq)
  1183.                         continue;
  1184.  
  1185.                 if (!pReceiverAIActor->IsPerceptionEnabled())
  1186.                         continue;
  1187.  
  1188.                 if (stim.flags & AISTIMPROC_ONLY_IF_VISIBLE)
  1189.                 {
  1190.                         if (!IsStimulusVisible(stim, pReceiverAIActor))
  1191.                                 continue;
  1192.                 }
  1193.  
  1194.                 IAISignalExtraData* pData = GetAISystem()->CreateSignalExtraData();
  1195.                 pData->point = stim.pos;
  1196.  
  1197.                 SetLastExplosionPosition(stim.pos, pReceiverAIActor);
  1198.                 pReceiverAIActor->SetSignal(0, "OnExposedToExplosion", 0, pData);
  1199.  
  1200.                 if (CPuppet* pReceiverPuppet = pReceiverAIActor->CastToCPuppet())
  1201.                         pReceiverPuppet->SetAlarmed();
  1202.  
  1203.                 RecordStimulusEvent(stim, 0.0f, *pReceiverAIActor);
  1204.         }
  1205. }
  1206.  
  1207. //===================================================================
  1208. // HandleBulletHit
  1209. //===================================================================
  1210. void CPerceptionManager::HandleBulletHit(const SStimulusRecord& stim)
  1211. {
  1212.         FUNCTION_PROFILER(gEnv->pSystem, PROFILE_AI);
  1213.  
  1214.         ActorLookUp& lookUp = *gAIEnv.pActorLookUp;
  1215.         lookUp.Prepare(ActorLookUp::Position | ActorLookUp::EntityID);
  1216.  
  1217.         IEntity* pShooterEnt = gEnv->pEntitySystem->GetEntity(stim.sourceId);
  1218.         CAIActor* pShooterActor = NULL;
  1219.         if (pShooterEnt)
  1220.                 pShooterActor = CastToCAIActorSafe(pShooterEnt->GetAI());
  1221.  
  1222.         // Send bullet events
  1223.         size_t activeCount = lookUp.GetActiveCount();
  1224.  
  1225.         for (size_t actorIndex = 0; actorIndex < activeCount; ++actorIndex)
  1226.         {
  1227.                 CAIActor* pReceiverAIActor = lookUp.GetActor<CAIActor>(actorIndex);
  1228.  
  1229.                 // Skip own fire.
  1230.                 if (stim.sourceId == lookUp.GetEntityID(actorIndex))
  1231.                         continue;
  1232.  
  1233.                 if (!pReceiverAIActor->IsPerceptionEnabled())
  1234.                         continue;
  1235.  
  1236.                 // Skip non-hostile bullets.
  1237.                 if (!pReceiverAIActor->IsHostile(pShooterActor))
  1238.                         continue;
  1239.  
  1240.                 AABB bounds;
  1241.                 pReceiverAIActor->GetEntity()->GetWorldBounds(bounds);
  1242.                 const float d = Distance::Point_AABBSq(stim.pos, bounds);
  1243.                 const float r = max(stim.radius, pReceiverAIActor->m_Parameters.m_PerceptionParams.bulletHitRadius);
  1244.  
  1245.                 if (d < sqr(r))
  1246.                 {
  1247.                         EntityId sourceId = 0;
  1248.                         if (pShooterActor)
  1249.                                 sourceId = pShooterActor->GetPerceivedEntityID();
  1250.                         else if (pShooterEnt)
  1251.                                 sourceId = pShooterEnt->GetId();
  1252.  
  1253.                         // Send event.
  1254.                         SAIEVENT event;
  1255.                         event.sourceId = sourceId;
  1256.                         if (pShooterActor)
  1257.                                 event.vPosition = pShooterActor->GetFirePos();
  1258.                         else if (pShooterEnt)
  1259.                                 event.vPosition = pShooterEnt->GetWorldPos();
  1260.                         else
  1261.                                 event.vPosition = stim.pos;
  1262.                         event.vStimPos = stim.pos;
  1263.                         event.nFlags = stim.flags;
  1264.                         event.fThreat = 1.0f; // pressureMultiplier
  1265.  
  1266.                         CPuppet* pActorPuppet = CastToCPuppetSafe(pShooterActor);
  1267.                         if (pActorPuppet)
  1268.                                 event.fThreat = pActorPuppet->GetCurrentWeaponDescriptor().pressureMultiplier;
  1269.  
  1270.                         pReceiverAIActor->Event(AIEVENT_ONBULLETRAIN, &event);
  1271.  
  1272.                         RecordStimulusEvent(stim, 0.0f, *pReceiverAIActor);
  1273.                 }
  1274.         }
  1275. }
  1276.  
  1277. //===================================================================
  1278. // HandleBulletWhizz
  1279. //===================================================================
  1280. void CPerceptionManager::HandleBulletWhizz(const SStimulusRecord& stim)
  1281. {
  1282.         FUNCTION_PROFILER(gEnv->pSystem, PROFILE_AI);
  1283.  
  1284.         ActorLookUp& lookUp = *gAIEnv.pActorLookUp;
  1285.         lookUp.Prepare(ActorLookUp::Position | ActorLookUp::EntityID);
  1286.  
  1287.         IEntity* pShooterEnt = gEnv->pEntitySystem->GetEntity(stim.sourceId);
  1288.         IAIObject* pShooterAI = pShooterEnt ? pShooterEnt->GetAI() : 0;
  1289.  
  1290.         Lineseg lof(stim.pos, stim.pos + stim.dir * stim.radius);
  1291.         float t;
  1292.  
  1293.         size_t activeCount = lookUp.GetActiveCount();
  1294.  
  1295.         for (size_t actorIndex = 0; actorIndex < activeCount; ++actorIndex)
  1296.         {
  1297.                 CAIActor* pReceiverAIActor = lookUp.GetActor<CAIActor>(actorIndex);
  1298.  
  1299.                 // Skip own fire.
  1300.                 if (lookUp.GetEntityID(actorIndex) == stim.sourceId)
  1301.                         continue;
  1302.  
  1303.                 const float r = 5.0f;
  1304.  
  1305.                 float d = Distance::Point_LinesegSq(lookUp.GetPosition(actorIndex), lof, t);
  1306.                 if (d < sqr(r))
  1307.                 {
  1308.                         if (!pReceiverAIActor->IsPerceptionEnabled())
  1309.                                 continue;
  1310.  
  1311.                         // Skip non-hostile bullets.
  1312.                         if (!pReceiverAIActor->IsHostile(pShooterAI))
  1313.                                 continue;
  1314.  
  1315.                         const Vec3 hitPos = lof.GetPoint(t);
  1316.                         SAIStimulus hitStim(AISTIM_BULLET_HIT, 0, stim.sourceId, 0, hitPos, ZERO, r);
  1317.                         RegisterStimulus(hitStim);
  1318.                 }
  1319.  
  1320.                 RecordStimulusEvent(stim, 0.0f, *pReceiverAIActor);
  1321.         }
  1322.  
  1323. }
  1324.  
  1325. //===================================================================
  1326. // HandleGrenade
  1327. //===================================================================
  1328. void CPerceptionManager::HandleGrenade(const SStimulusRecord& stim)
  1329. {
  1330.         FUNCTION_PROFILER(gEnv->pSystem, PROFILE_AI);
  1331.  
  1332.         EntityId shooterId = stim.sourceId;
  1333.  
  1334.         IEntity* pShooter = gEnv->pEntitySystem->GetEntity(shooterId);
  1335.         if (!pShooter)
  1336.                 return;
  1337.  
  1338.         ActorLookUp& lookUp = *gAIEnv.pActorLookUp;
  1339.         lookUp.Prepare(ActorLookUp::Position | ActorLookUp::EntityID);
  1340.  
  1341.         CAIActor* pShooterActor = 0;
  1342.         if (pShooter)
  1343.                 pShooterActor = CastToCAIActorSafe(pShooter->GetAI());
  1344.  
  1345.         switch (stim.subType)
  1346.         {
  1347.         case AIGRENADE_THROWN:
  1348.                 {
  1349.                         float radSq = sqr(stim.radius);
  1350.                         Vec3 throwPos = pShooterActor ? pShooterActor->GetFirePos() : stim.pos; // Grenade position
  1351.                         // Inform the AI that sees the throw
  1352.                         size_t activeCount = lookUp.GetActiveCount();
  1353.  
  1354.                         for (size_t actorIndex = 0; actorIndex < activeCount; ++actorIndex)
  1355.                         {
  1356.                                 CAIActor* pAIActor = lookUp.GetActor<CAIActor>(actorIndex);
  1357.                                 if (lookUp.GetEntityID(actorIndex) == shooterId)
  1358.                                         continue;
  1359.  
  1360.                                 const Vec3 vAIActorPos = lookUp.GetPosition(actorIndex);
  1361.  
  1362.                                 // If the puppet is not close to the predicted position, skip.
  1363.                                 if (Distance::Point_PointSq(stim.pos, vAIActorPos) > radSq)
  1364.                                         continue;
  1365.  
  1366.                                 if (!pAIActor->IsPerceptionEnabled())
  1367.                                         continue;
  1368.  
  1369.                                 // Inform enemies only.
  1370.                                 const bool isShootherFriendly = pShooterActor ? !pShooterActor->IsHostile(pAIActor) : false;
  1371.                                 if (isShootherFriendly)
  1372.                                         continue;
  1373.  
  1374.                                 // Only sense grenades that are on front of the AI and visible when thrown.
  1375.                                 // Another signal is sent when the grenade hits the ground.
  1376.                                 Vec3 delta = throwPos - vAIActorPos;  // grenade to AI
  1377.                                 float dist = delta.NormalizeSafe();
  1378.                                 const float thr = cosf(DEG2RAD(160.0f));
  1379.                                 if (delta.Dot(pAIActor->GetViewDir()) > thr)
  1380.                                 {
  1381.                                         static const int objTypes = ent_static | ent_terrain | ent_rigid | ent_sleeping_rigid;
  1382.                                         static const unsigned int flags = rwi_stop_at_pierceable | rwi_colltype_any;
  1383.                                         const RayCastResult& result = gAIEnv.pRayCaster->Cast(RayCastRequest(vAIActorPos, delta * dist, objTypes, flags));
  1384.  
  1385.                                         if (result)
  1386.                                                 throwPos = result[0].pt;
  1387.  
  1388.                                         GetAISystem()->AddDebugLine(vAIActorPos, throwPos, 255, 0, 0, 15.0f);
  1389.  
  1390.                                         if (!result || result[0].dist > dist * 0.9f)
  1391.                                         {
  1392.                                                 IAISignalExtraData* pEData = GetAISystem()->CreateSignalExtraData(); // no leak - this will be deleted inside SendAnonymousSignal
  1393.                                                 pEData->point = stim.pos;                                            // avoid predicted pos
  1394.                                                 pEData->nID = shooterId;
  1395.                                                 pEData->iValue = 1;
  1396.  
  1397.                                                 SetLastExplosionPosition(stim.pos, pAIActor);
  1398.                                                 GetAISystem()->SendSignal(SIGNALFILTER_SENDER, 1, "OnGrenadeDanger", pAIActor, pEData);
  1399.  
  1400.                                                 RecordStimulusEvent(stim, 0.0f, *pAIActor);
  1401.                                         }
  1402.                                 }
  1403.                         }
  1404.                 }
  1405.                 break;
  1406.         case AIGRENADE_COLLISION:
  1407.                 {
  1408.                         float radSq = sqr(stim.radius);
  1409.  
  1410.                         size_t activeCount = lookUp.GetActiveCount();
  1411.  
  1412.                         for (size_t actorIndex = 0; actorIndex < activeCount; ++actorIndex)
  1413.                         {
  1414.                                 CAIActor* pAIActor = lookUp.GetActor<CAIActor>(actorIndex);
  1415.  
  1416.                                 // If the puppet is not close to the grenade, skip.
  1417.                                 if (Distance::Point_PointSq(stim.pos, lookUp.GetPosition(actorIndex)) > radSq)
  1418.                                         continue;
  1419.  
  1420.                                 IAISignalExtraData* pEData = GetAISystem()->CreateSignalExtraData();  // no leak - this will be deleted inside SendAnonymousSignal
  1421.                                 pEData->point = stim.pos;
  1422.                                 pEData->nID = shooterId;
  1423.                                 pEData->iValue = 2;
  1424.  
  1425.                                 SetLastExplosionPosition(stim.pos, pAIActor);
  1426.                                 GetAISystem()->SendSignal(SIGNALFILTER_SENDER, 1, "OnGrenadeDanger", pAIActor, pEData);
  1427.  
  1428.                                 RecordStimulusEvent(stim, 0.0f, *pAIActor);
  1429.                         }
  1430.                 }
  1431.                 break;
  1432.         case AIGRENADE_FLASH_BANG:
  1433.                 {
  1434.                         float radSq = sqr(stim.radius);
  1435.  
  1436.                         size_t activeCount = lookUp.GetActiveCount();
  1437.  
  1438.                         for (size_t actorIndex = 0; actorIndex < activeCount; ++actorIndex)
  1439.                         {
  1440.                                 CAIActor* pAIActor = lookUp.GetActor<CAIActor>(actorIndex);
  1441.                                 if (lookUp.GetEntityID(actorIndex) == shooterId)
  1442.                                         continue;
  1443.  
  1444.                                 const Vec3 vAIActorPos = lookUp.GetPosition(actorIndex);
  1445.  
  1446.                                 // If AI Actor is not close to the flash, skip.
  1447.                                 if (Distance::Point_PointSq(stim.pos, vAIActorPos) > radSq)
  1448.                                         continue;
  1449.  
  1450.                                 // Only sense grenades that are on front of the AI and visible when thrown.
  1451.                                 // Another signal is sent when the grenade hits the ground.
  1452.                                 Vec3 delta = stim.pos - vAIActorPos;  // grenade to AI
  1453.                                 float dist = delta.NormalizeSafe();
  1454.                                 const float thr = cosf(DEG2RAD(160.0f));
  1455.                                 if (delta.Dot(pAIActor->GetViewDir()) > thr)
  1456.                                 {
  1457.                                         static const int objTypes = ent_static | ent_terrain | ent_rigid | ent_sleeping_rigid;
  1458.                                         static const unsigned int flags = rwi_stop_at_pierceable | rwi_colltype_any;
  1459.  
  1460.                                         if (!gAIEnv.pRayCaster->Cast(RayCastRequest(vAIActorPos, delta * dist, objTypes, flags)))
  1461.                                         {
  1462.                                                 IAISignalExtraData* pExtraData = GetAISystem()->CreateSignalExtraData();
  1463.                                                 pExtraData->iValue = 0;
  1464.                                                 GetAISystem()->SendSignal(SIGNALFILTER_SENDER, 1, "OnExposedToFlashBang", pAIActor, pExtraData);
  1465.  
  1466.                                                 RecordStimulusEvent(stim, 0.0f, *pAIActor);
  1467.                                         }
  1468.                                 }
  1469.                         }
  1470.                 }
  1471.                 break;
  1472.         case AIGRENADE_SMOKE:
  1473.                 {
  1474.                         float radSq = sqr(stim.radius);
  1475.                         size_t activeCount = lookUp.GetActiveCount();
  1476.  
  1477.                         for (size_t actorIndex = 0; actorIndex < activeCount; ++actorIndex)
  1478.                         {
  1479.                                 CAIActor* pAIActor = lookUp.GetActor<CAIActor>(actorIndex);
  1480.  
  1481.                                 // If the puppet is not close to the smoke, skip.
  1482.                                 if (Distance::Point_PointSq(stim.pos, lookUp.GetPosition(actorIndex)) > radSq)
  1483.                                         continue;
  1484.  
  1485.                                 GetAISystem()->SendSignal(SIGNALFILTER_SENDER, 1, "OnExposedToSmoke", pAIActor);
  1486.  
  1487.                                 RecordStimulusEvent(stim, 0.0f, *pAIActor);
  1488.                         }
  1489.                 }
  1490.                 break;
  1491.         default:
  1492.                 break;
  1493.         }
  1494. }
  1495.  
  1496. void CPerceptionManager::SetLastExplosionPosition(const Vec3& position, CAIActor* pAIActor) const
  1497. {
  1498.         assert(pAIActor);
  1499.         if (pAIActor)
  1500.         {
  1501.                 IScriptTable* pActorScriptTable = pAIActor->GetEntity()->GetScriptTable();
  1502.                 Vec3 tempPos(ZERO);
  1503.                 if (pActorScriptTable->GetValue("lastExplosiveThreatPos", tempPos))
  1504.                 {
  1505.                         pActorScriptTable->SetValue("lastExplosiveThreatPos", position);
  1506.                 }
  1507.         }
  1508.  
  1509. }
  1510.  
  1511. //===================================================================
  1512. // RegisterAIEventListener
  1513. //===================================================================
  1514. void CPerceptionManager::RegisterAIEventListener(IAIEventListener* pListener, const Vec3& pos, float rad, int flags)
  1515. {
  1516.         if (!pListener)
  1517.                 return;
  1518.  
  1519.         // Check if the listener exists
  1520.         for (unsigned i = 0, ni = m_eventListeners.size(); i < ni; ++i)
  1521.         {
  1522.                 SAIEventListener& listener = m_eventListeners[i];
  1523.                 if (listener.pListener == pListener)
  1524.                 {
  1525.                         listener.pos = pos;
  1526.                         listener.radius = rad;
  1527.                         listener.flags = flags;
  1528.                         return;
  1529.                 }
  1530.         }
  1531.  
  1532.         m_eventListeners.resize(m_eventListeners.size() + 1);
  1533.         SAIEventListener& listener = m_eventListeners.back();
  1534.         listener.pListener = pListener;
  1535.         listener.pos = pos;
  1536.         listener.radius = rad;
  1537.         listener.flags = flags;
  1538. }
  1539.  
  1540. //===================================================================
  1541. // UnregisterAIEventListener
  1542. //===================================================================
  1543. void CPerceptionManager::UnregisterAIEventListener(IAIEventListener* pListener)
  1544. {
  1545.         if (!pListener)
  1546.                 return;
  1547.  
  1548.         // Check if the listener exists
  1549.         for (unsigned i = 0, ni = m_eventListeners.size(); i < ni; ++i)
  1550.         {
  1551.                 SAIEventListener& listener = m_eventListeners[i];
  1552.                 if (listener.pListener == pListener)
  1553.                 {
  1554.                         m_eventListeners[i] = m_eventListeners.back();
  1555.                         m_eventListeners.pop_back();
  1556.                         return;
  1557.                 }
  1558.         }
  1559. }
  1560.  
  1561. //===================================================================
  1562. // IsPointInRadiusOfStimulus
  1563. //===================================================================
  1564. bool CPerceptionManager::IsPointInRadiusOfStimulus(EAIStimulusType type, const Vec3& pos) const
  1565. {
  1566.         const std::vector<SStimulusRecord>& stims = m_stimuli[(int)type];
  1567.         for (unsigned i = 0, ni = stims.size(); i < ni; ++i)
  1568.         {
  1569.                 const SStimulusRecord& s = stims[i];
  1570.                 if (Distance::Point_PointSq(s.pos, pos) < sqr(s.radius))
  1571.                         return true;
  1572.         }
  1573.         return false;
  1574. }
  1575.  
  1576. //===================================================================
  1577. // NotifyAIEventListeners
  1578. //===================================================================
  1579. void CPerceptionManager::NotifyAIEventListeners(const SStimulusRecord& stim, float threat)
  1580. {
  1581.         FUNCTION_PROFILER(gEnv->pSystem, PROFILE_AI);
  1582.  
  1583.         int flags = 1 << (int)stim.type;
  1584.  
  1585.         for (unsigned i = 0, ni = m_eventListeners.size(); i < ni; ++i)
  1586.         {
  1587.                 SAIEventListener& listener = m_eventListeners[i];
  1588.                 if ((listener.flags & flags) != 0 && Distance::Point_PointSq(stim.pos, listener.pos) < sqr(listener.radius + stim.radius))
  1589.                         listener.pListener->OnAIEvent((EAIStimulusType)stim.type, stim.pos, stim.radius, threat, stim.sourceId);
  1590.         }
  1591. }
  1592.  
  1593. //===================================================================
  1594. // SupressSound
  1595. //===================================================================
  1596. float CPerceptionManager::SupressSound(const Vec3& pos, float radius)
  1597. {
  1598.         float minScale = 1.0f;
  1599.         AIObjectOwners::iterator ai = gAIEnv.pAIObjectManager->m_Objects.find(AIOBJECT_SNDSUPRESSOR), aiend = gAIEnv.pAIObjectManager->m_Objects.end();
  1600.         for (; ai != aiend; ++ai)
  1601.         {
  1602.                 if (ai->first != AIOBJECT_SNDSUPRESSOR)
  1603.                         break;
  1604.  
  1605.                 CAIObject* obj = ai->second.GetAIObject();
  1606.                 if (!obj->IsEnabled())
  1607.                         continue;
  1608.  
  1609.                 const float r = obj->GetRadius();
  1610.                 const float silenceRadius = r * 0.3f;
  1611.                 const float distSqr = Distance::Point_PointSq(pos, obj->GetPos());
  1612.  
  1613.                 if (distSqr > sqr(r))
  1614.                         continue;
  1615.  
  1616.                 if (distSqr < sqr(silenceRadius))
  1617.                         return 0.0f;
  1618.  
  1619.                 const float dist = sqrtf(distSqr);
  1620.                 const float scale = (dist - silenceRadius) / (r - silenceRadius);
  1621.                 minScale = min(minScale, scale);
  1622.         }
  1623.  
  1624.         return radius * minScale;
  1625. }
  1626.  
  1627. //===================================================================
  1628. // IsSoundOccluded
  1629. //===================================================================
  1630. bool CPerceptionManager::IsSoundOccluded(CAIActor* pAIActor, const Vec3& vSoundPos)
  1631. {
  1632.         FUNCTION_PROFILER(gEnv->pSystem, PROFILE_AI);
  1633.  
  1634.         // TODO: Check if this function is necessary at all!
  1635.  
  1636.         int nBuildingIDPuppet = -1, nBuildingIDSound = -1;
  1637.  
  1638.         Vec3 actorPos(pAIActor->GetPos());
  1639.  
  1640.         gAIEnv.pNavigation->CheckNavigationType(actorPos, nBuildingIDPuppet, pAIActor->m_movementAbility.pathfindingProperties.navCapMask);
  1641.         gAIEnv.pNavigation->CheckNavigationType(vSoundPos, nBuildingIDSound, pAIActor->m_movementAbility.pathfindingProperties.navCapMask);
  1642.  
  1643.         // Sound and puppet both are outdoor, not occluded.
  1644.         if (nBuildingIDPuppet < 0 && nBuildingIDSound < 0)
  1645.                 return false;
  1646.  
  1647.         if (nBuildingIDPuppet != nBuildingIDSound)
  1648.         {
  1649.                 if (nBuildingIDPuppet < 0)
  1650.                 {
  1651.                         // Puppet is outdoors, sound indoors
  1652.                         if (IVisArea* pAreaSound = gEnv->p3DEngine->GetVisAreaFromPos(vSoundPos))
  1653.                         {
  1654.                                 // Occluded if the area is not connected to outdoors.
  1655.                                 return !pAreaSound->IsConnectedToOutdoor();
  1656.                         }
  1657.                 }
  1658.                 else if (nBuildingIDSound < 0)
  1659.                 {
  1660.                         // Sound is outdoors, puppet indoors
  1661.                         if (IVisArea* pAreaPuppet = gEnv->p3DEngine->GetVisAreaFromPos(actorPos))
  1662.                         {
  1663.                                 // Occluded if the area is not connected to outdoors.
  1664.                                 return !pAreaPuppet->IsConnectedToOutdoor();
  1665.                         }
  1666.                 }
  1667.                 else
  1668.                 {
  1669.                         // If in two different buildings we cannot hear the sound for sure.
  1670.                         // Require one building to have visarea, though.
  1671.                         if (gEnv->p3DEngine->GetVisAreaFromPos(actorPos) || gEnv->p3DEngine->GetVisAreaFromPos(vSoundPos))
  1672.                                 return true;
  1673.                 }
  1674.         }
  1675.  
  1676.         // Not occluded
  1677.         return false;
  1678. }
  1679.  
  1680. //===================================================================
  1681. // RayOcclusionPlaneIntersection
  1682. //===================================================================
  1683. int CPerceptionManager::RayOcclusionPlaneIntersection(const Vec3& start, const Vec3& end)
  1684. {
  1685.         const ShapeMap& occPlanes = GetAISystem()->GetOcclusionPlanes();
  1686.  
  1687.         if (occPlanes.empty())
  1688.                 return 0;
  1689.  
  1690.         ShapeMap::const_iterator di = occPlanes.begin(), diend = occPlanes.end();
  1691.         for (; di != diend; ++di)
  1692.         {
  1693.                 if (!di->second.shape.empty())
  1694.                 {
  1695.                         float fShapeHeight = ((di->second.shape).front()).z;
  1696.                         if ((start.z < fShapeHeight) && (end.z < fShapeHeight))
  1697.                                 continue;
  1698.                         if ((start.z > fShapeHeight) && (end.z > fShapeHeight))
  1699.                                 continue;
  1700.  
  1701.                         // find out where ray hits horizontal plane fShapeHeigh (with a nasty hack)
  1702.                         Vec3 vIntersection;
  1703.                         float t = (start.z - fShapeHeight) / (start.z - end.z);
  1704.                         vIntersection = start + t * (end - start);
  1705.  
  1706.                         // is it inside the polygon?
  1707.                         if (Overlap::Point_Polygon2D(vIntersection, di->second.shape, &di->second.aabb))
  1708.                                 return 1;
  1709.                 }
  1710.         }
  1711.  
  1712.         return 0;
  1713. }
  1714.  
  1715. //===================================================================
  1716. // Serialize
  1717. //===================================================================
  1718. void CPerceptionManager::Serialize(TSerialize ser)
  1719. {
  1720.         ser.BeginGroup("PerceptionManager");
  1721.         for (unsigned i = 0; i < AI_MAX_STIMULI; ++i)
  1722.         {
  1723.                 char name[64];
  1724.                 cry_sprintf(name, "m_stimuli%02d", i);
  1725.                 ser.Value(name, m_stimuli[i]);
  1726.                 cry_sprintf(name, "m_ignoreStimuliFrom%02d", i);
  1727.                 ser.Value(name, m_ignoreStimuliFrom[i]);
  1728.         }
  1729.         ser.EndGroup();
  1730. }
  1731.  
  1732. //===================================================================
  1733. // RecordStimulusEvent
  1734. //===================================================================
  1735. void CPerceptionManager::RecordStimulusEvent(const SStimulusRecord& stim, float fRadius, IAIObject& receiver) const
  1736. {
  1737. #ifdef CRYAISYSTEM_DEBUG
  1738.         stack_string sType = g_szAIStimulusType[stim.type];
  1739.         if (stim.type == AISTIM_SOUND)
  1740.                 sType += g_szAISoundStimType[stim.subType];
  1741.         else if (stim.type == AISTIM_GRENADE)
  1742.                 sType += g_szAIGrenadeStimType[stim.subType];
  1743.  
  1744.         IEntity* pSource = gEnv->pEntitySystem->GetEntity(stim.sourceId);
  1745.         IEntity* pTarget = gEnv->pEntitySystem->GetEntity(stim.targetId);
  1746.         stack_string sDebugLine;
  1747.         sDebugLine.Format("%s from %s to %s", sType.c_str(), pSource ? pSource->GetName() : "Unknown", pTarget ? pTarget->GetName() : "All");
  1748.  
  1749.         // Record event
  1750.         if (gEnv->pAISystem->IsRecording(&receiver, IAIRecordable::E_REGISTERSTIMULUS))
  1751.         {
  1752.                 gEnv->pAISystem->Record(&receiver, IAIRecordable::E_REGISTERSTIMULUS, sDebugLine.c_str());
  1753.         }
  1754.  
  1755.         IAIRecordable::RecorderEventData recorderEventData(sDebugLine.c_str());
  1756.         receiver.RecordEvent(IAIRecordable::E_REGISTERSTIMULUS, &recorderEventData);
  1757. #endif
  1758. }
  1759.  
  1760. bool CPerceptionManager::IsStimulusVisible(const SStimulusRecord& stim, const CAIActor* pAIActor)
  1761. {
  1762.         Vec3 stimPos = stim.pos;
  1763.         const Vec3 vAIActorPos = pAIActor->GetPos();
  1764.         Vec3 delta = stimPos - vAIActorPos;
  1765.         float dist = delta.NormalizeSafe();
  1766.         const float thr = cosf(DEG2RAD(110.0f));
  1767.         if (delta.dot(pAIActor->GetViewDir()) > thr)
  1768.         {
  1769.                 static const int objTypes = ent_static | ent_terrain | ent_rigid | ent_sleeping_rigid;
  1770.                 static const unsigned int flags = rwi_stop_at_pierceable | rwi_colltype_any;
  1771.                 const RayCastResult& result = gAIEnv.pRayCaster->Cast(RayCastRequest(vAIActorPos, delta * dist, objTypes, flags));
  1772.  
  1773.                 if (result)
  1774.                         stimPos = result[0].pt;
  1775.  
  1776.                 GetAISystem()->AddDebugLine(vAIActorPos, stimPos, 255, 0, 0, 15.0f);
  1777.  
  1778.                 if (!result || result[0].dist > dist * 0.9f)
  1779.                 {
  1780.                         return true;
  1781.                 }
  1782.         }
  1783.  
  1784.         return false;
  1785. }
  1786.  
downloadPerceptionManager.cpp Source code - Download CRYENGINE Source code
Related Source Codes/Software:
postal - 2017-06-11
reactide - Reactide is the first dedicated IDE for React web ... 2017-06-11
rkt - rkt is a pod-native container engine for Linux. It... 2017-06-11
uWebSockets - Tiny WebSockets https://for... 2017-06-11
realworld - TodoMVC for the RealWorld - Exemplary fullstack Me... 2017-06-11
CRYENGINE - CRYENGINE is a powerful real-time game development... 2017-06-11
goreplay - GoReplay is an open-source tool for capturing and ... 2017-06-10
pyenv - Simple Python version management 2017-06-10
redux-saga - An alternative side effect model for Redux apps ... 2017-06-10
angular-starter - 2017-06-10

 Back to top