BVB Source Codes

CRYENGINE Show GoalOp.cpp Source code

Return Download CRYENGINE: download GoalOp.cpp Source code - Download CRYENGINE Source code - Type:.cpp
  1. // Copyright 2001-2016 Crytek GmbH / Crytek Group. All rights reserved.
  2.  
  3. #include "StdAfx.h"
  4. #include "GoalOp.h"
  5. #include "GoalOpTrace.h"
  6. #include "Puppet.h"
  7. #include "AIVehicle.h"
  8. #include "AILog.h"
  9. #include <CrySystem/IConsole.h>
  10. #include "AICollision.h"
  11. #include "NavRegion.h"
  12. #include "PipeUser.h"
  13. #include "Leader.h"
  14. #include "DebugDrawContext.h"
  15. #include "PathFollower.h"
  16.  
  17. #include <CrySystem/ISystem.h>
  18. #include <CrySystem/ITimer.h>
  19. #include <CryPhysics/IPhysics.h>
  20. #include <CryMath/Cry_Math.h>
  21. #include <CrySystem/ILog.h>
  22. #include <CryNetwork/ISerialize.h>
  23. #include "TacticalPointSystem/TacticalPointSystem.h"
  24.  
  25. #include "Communication/CommunicationManager.h"
  26.  
  27. #include "GameSpecific/GoalOp_Crysis2.h"
  28.  
  29. //#pragma optimize("", off)
  30. //#pragma inline_depth(0)
  31.  
  32. // Ugly
  33. #define C_MaxDistanceForPathOffset 2 // threshold (in m) used in COPStick and COPApproach, to detect if the returned path
  34. // is bringing the agent too far from the expected destination
  35.  
  36. CGoalOpParallel::~CGoalOpParallel()
  37. {
  38.         SAFE_DELETE(m_NextConcurrentPipe);
  39.         SAFE_DELETE(m_ConcurrentPipe);
  40. }
  41.  
  42. void CGoalOpParallel::ParseParams(const GoalParams& node)  //(const XmlNodeRef &node)
  43. {
  44.         uint32 count = node.GetChildCount();//node->getChildCount();
  45.         for (uint32 loop = 0; loop < count; ++loop)
  46.         {
  47.                 const GoalParams& child = node.GetChild(loop);
  48.                 ParseParam(child.GetName(), child);
  49.         }
  50.  
  51.         if (m_NextConcurrentPipe)
  52.         {
  53.                 m_NextConcurrentPipe->ParseParams(node);
  54.         }
  55.         else if (m_ConcurrentPipe)
  56.         {
  57.                 m_ConcurrentPipe->ParseParams(node);
  58.         }
  59. }
  60.  
  61. void CGoalOpParallel::SetConcurrentPipe(CGoalPipe* goalPipe)
  62. {
  63.         if (m_ConcurrentPipe)
  64.         {
  65.                 m_NextConcurrentPipe = goalPipe;
  66.         }
  67.         else
  68.         {
  69.                 m_ConcurrentPipe = goalPipe;
  70.  
  71.                 if (m_ConcurrentPipe)
  72.                 {
  73.                         m_OpInfos.resize(0);
  74.                         uint32 count = m_ConcurrentPipe->GetNumGoalOps();
  75.  
  76.                         m_OpInfos.resize(count);
  77.  
  78.                         for (uint32 loop = 0; loop < count; ++loop)
  79.                         {
  80.                                 m_OpInfos[loop].blocking = m_ConcurrentPipe->IsGoalBlocking(loop);
  81.                         }
  82.                 }
  83.         }
  84. }
  85.  
  86. void CGoalOpParallel::ReleaseConcurrentPipe(CPipeUser* pPipeUser, bool clearNextPipe)
  87. {
  88.         if (m_ConcurrentPipe)
  89.         {
  90.                 m_ConcurrentPipe->ResetGoalops(pPipeUser);
  91.                 pPipeUser->NotifyListeners(m_ConcurrentPipe, ePN_Finished);
  92.                 delete m_ConcurrentPipe;
  93.                 m_ConcurrentPipe = 0;
  94.         }
  95.  
  96.         if (clearNextPipe && m_NextConcurrentPipe)
  97.         {
  98.                 m_NextConcurrentPipe->ResetGoalops(pPipeUser);
  99.                 pPipeUser->NotifyListeners(m_NextConcurrentPipe, ePN_Finished);
  100.                 delete m_NextConcurrentPipe;
  101.                 m_NextConcurrentPipe = 0;
  102.         }
  103. }
  104.  
  105. EGoalOpResult CGoalOpParallel::Execute(CPipeUser* pPipeUser)
  106. {
  107.         uint32 executed = 0;
  108.  
  109.         if (m_NextConcurrentPipe)
  110.         {
  111.                 ReleaseConcurrentPipe(pPipeUser);
  112.                 SetConcurrentPipe(m_NextConcurrentPipe);
  113.                 m_NextConcurrentPipe = 0;
  114.         }
  115.  
  116.         if (m_ConcurrentPipe)
  117.         {
  118.                 uint32 count = m_ConcurrentPipe->GetNumGoalOps();
  119.  
  120.                 for (uint32 loop = 0; loop < count; ++loop)
  121.                 {
  122.                         CGoalOp* op = m_ConcurrentPipe->GetGoalOp(loop);
  123.  
  124.                         if ((op != NULL) && (!m_OpInfos[loop].done))
  125.                         {
  126.                                 EGoalOpResult result = op->Execute(pPipeUser);
  127.                                 ++executed;
  128.  
  129.                                 if (result != eGOR_IN_PROGRESS)
  130.                                 {
  131.                                         --executed;
  132.                                         m_OpInfos[loop].done = true;
  133.                                 }
  134.                                 else if (m_OpInfos[loop].blocking)
  135.                                 {
  136.                                         break;
  137.                                 }
  138.                         }
  139.                 }
  140.         }
  141.  
  142.         return (m_ConcurrentPipe == 0) ? eGOR_NONE : (executed > 0) ? eGOR_IN_PROGRESS : eGOR_DONE;
  143. }
  144.  
  145. void CGoalOpParallel::ExecuteDry(CPipeUser* pPipeUser)
  146. {
  147. };
  148.  
  149. CGoalOpXMLReader CGoalOp::s_xml;
  150.  
  151. CGoalOpXMLReader::CGoalOpXMLReader()
  152. {
  153.         m_dictAIObjectType.Add("None", AIOBJECT_NONE);
  154.         m_dictAIObjectType.Add("Dummy", AIOBJECT_DUMMY);
  155.         m_dictAIObjectType.Add("Actor", AIOBJECT_ACTOR);
  156.         m_dictAIObjectType.Add("Vehicle", AIOBJECT_VEHICLE);
  157.         m_dictAIObjectType.Add("Target", AIOBJECT_TARGET);
  158.         m_dictAIObjectType.Add("Aware", AIOBJECT_AWARE);
  159.         m_dictAIObjectType.Add("Attribute", AIOBJECT_ATTRIBUTE);
  160.         m_dictAIObjectType.Add("WayPoint", AIOBJECT_WAYPOINT);
  161.         m_dictAIObjectType.Add("HidePoint", AIOBJECT_HIDEPOINT);
  162.         m_dictAIObjectType.Add("SoundSupressor", AIOBJECT_SNDSUPRESSOR);
  163.         m_dictAIObjectType.Add("Helicopter", AIOBJECT_HELICOPTER);
  164.         m_dictAIObjectType.Add("Infected", AIOBJECT_INFECTED);
  165.         m_dictAIObjectType.Add("AlienTick", AIOBJECT_ALIENTICK);
  166.         m_dictAIObjectType.Add("Car", AIOBJECT_CAR);
  167.         m_dictAIObjectType.Add("Boat", AIOBJECT_BOAT);
  168.         m_dictAIObjectType.Add("Airplane", AIOBJECT_AIRPLANE);
  169.         m_dictAIObjectType.Add("2DFly", AIOBJECT_2D_FLY);
  170.         m_dictAIObjectType.Add("MountedWeapon", AIOBJECT_MOUNTEDWEAPON);
  171.         m_dictAIObjectType.Add("GlobalAlertness", AIOBJECT_GLOBALALERTNESS);
  172.         m_dictAIObjectType.Add("Leader", AIOBJECT_LEADER);
  173.         m_dictAIObjectType.Add("Order", AIOBJECT_ORDER);
  174.         m_dictAIObjectType.Add("Player", AIOBJECT_PLAYER);
  175.         m_dictAIObjectType.Add("Grenade", AIOBJECT_GRENADE);
  176.         m_dictAIObjectType.Add("RPG", AIOBJECT_RPG);
  177.  
  178.         m_dictAnimationMode.Add("Signal", AIANIM_SIGNAL);
  179.         m_dictAnimationMode.Add("Action", AIANIM_ACTION);
  180.  
  181.         m_dictBools.Add("false", false);
  182.         m_dictBools.Add("true", true);
  183.  
  184.         m_dictCoverLocation.Add("None", eCUL_None);
  185.         m_dictCoverLocation.Add("Automatic", eCUL_Automatic);
  186.         m_dictCoverLocation.Add("Left", eCUL_Left);
  187.         m_dictCoverLocation.Add("Right", eCUL_Right);
  188.         m_dictCoverLocation.Add("Center", eCUL_Center);
  189.  
  190.         m_dictFireMode.Add("Off", FIREMODE_OFF);
  191.         m_dictFireMode.Add("Burst", FIREMODE_BURST);
  192.         m_dictFireMode.Add("Continuous", FIREMODE_CONTINUOUS);
  193.         m_dictFireMode.Add("Forced", FIREMODE_FORCED);
  194.         m_dictFireMode.Add("Aim", FIREMODE_AIM);
  195.         m_dictFireMode.Add("Secondary", FIREMODE_SECONDARY);
  196.         m_dictFireMode.Add("SecondarySmoke", FIREMODE_SECONDARY_SMOKE);
  197.         m_dictFireMode.Add("Melee", FIREMODE_MELEE);
  198.         m_dictFireMode.Add("Kill", FIREMODE_KILL);
  199.         m_dictFireMode.Add("BurstWhileMoving", FIREMODE_BURST_WHILE_MOVING);
  200.         m_dictFireMode.Add("PanicSpread", FIREMODE_PANIC_SPREAD);
  201.         m_dictFireMode.Add("BurstDrawFire", FIREMODE_BURST_DRAWFIRE);
  202.         m_dictFireMode.Add("MeleeForced", FIREMODE_MELEE_FORCED);
  203.         m_dictFireMode.Add("BurstSnipe", FIREMODE_BURST_SNIPE);
  204.         m_dictFireMode.Add("AimSweep", FIREMODE_AIM_SWEEP);
  205.         m_dictFireMode.Add("BurstOnce", FIREMODE_BURST_ONCE);
  206.  
  207.         m_dictLook.Add("Look", AILOOKMOTIVATION_LOOK);
  208.         m_dictLook.Add("Glance", AILOOKMOTIVATION_GLANCE);
  209.         m_dictLook.Add("Startle", AILOOKMOTIVATION_STARTLE);
  210.         m_dictLook.Add("DoubleTake", AILOOKMOTIVATION_DOUBLETAKE);
  211.  
  212.         m_dictRegister.Add("LastOp", AI_REG_LASTOP);
  213.         m_dictRegister.Add("RefPoint", AI_REG_REFPOINT);
  214.         m_dictRegister.Add("AttTarget", AI_REG_ATTENTIONTARGET);
  215.         m_dictRegister.Add("Path", AI_REG_PATH);
  216.         m_dictRegister.Add("Cover", AI_REG_COVER);
  217.  
  218.         m_dictSignalFilter.Add("Sender", SIGNALFILTER_SENDER);
  219.         m_dictSignalFilter.Add("LastOp", SIGNALFILTER_LASTOP);
  220.         m_dictSignalFilter.Add("GroupOnly", SIGNALFILTER_GROUPONLY);
  221.         m_dictSignalFilter.Add("FactionOnly", SIGNALFILTER_FACTIONONLY);
  222.         m_dictSignalFilter.Add("AnyoneInComm", SIGNALFILTER_ANYONEINCOMM);
  223.         m_dictSignalFilter.Add("Target", SIGNALFILTER_TARGET);
  224.         m_dictSignalFilter.Add("SuperGroup", SIGNALFILTER_SUPERGROUP);
  225.         m_dictSignalFilter.Add("SuperFaction", SIGNALFILTER_SUPERFACTION);
  226.         m_dictSignalFilter.Add("SuperTarget", SIGNALFILTER_SUPERTARGET);
  227.         m_dictSignalFilter.Add("NearestGroup", SIGNALFILTER_NEARESTGROUP);
  228.         m_dictSignalFilter.Add("NearestSpecies", SIGNALFILTER_NEARESTSPECIES);
  229.         m_dictSignalFilter.Add("NearestInComm", SIGNALFILTER_NEARESTINCOMM);
  230.         m_dictSignalFilter.Add("HalfOfGroup", SIGNALFILTER_HALFOFGROUP);
  231.         m_dictSignalFilter.Add("Leader", SIGNALFILTER_LEADER);
  232.         m_dictSignalFilter.Add("GroupOnlyExcept", SIGNALFILTER_GROUPONLY_EXCEPT);
  233.         m_dictSignalFilter.Add("AnyoneInCommExcept", SIGNALFILTER_ANYONEINCOMM_EXCEPT);
  234.         m_dictSignalFilter.Add("LeaderEntity", SIGNALFILTER_LEADERENTITY);
  235.         m_dictSignalFilter.Add("NearestInCommFaction", SIGNALFILTER_NEARESTINCOMM_FACTION);
  236.         m_dictSignalFilter.Add("NearestInCommLooking", SIGNALFILTER_NEARESTINCOMM_LOOKING);
  237.         m_dictSignalFilter.Add("Formation", SIGNALFILTER_FORMATION);
  238.         m_dictSignalFilter.Add("FormationExcept", SIGNALFILTER_FORMATION_EXCEPT);
  239.         m_dictSignalFilter.Add("Readability", SIGNALFILTER_READABILITY);
  240.         m_dictSignalFilter.Add("ReadabilityAnticipation", SIGNALFILTER_READABILITYAT);
  241.         m_dictSignalFilter.Add("ReadabilityResponse", SIGNALFILTER_READABILITYRESPONSE);
  242.  
  243.         m_dictStance.Add("Null", STANCE_NULL);
  244.         m_dictStance.Add("Stand", STANCE_STAND);
  245.         m_dictStance.Add("Crouch", STANCE_CROUCH);
  246.         m_dictStance.Add("Prone", STANCE_PRONE);
  247.         m_dictStance.Add("Relaxed", STANCE_RELAXED);
  248.         m_dictStance.Add("Stealth", STANCE_STEALTH);
  249.         m_dictStance.Add("Alerted", STANCE_ALERTED);
  250.         m_dictStance.Add("LowCover", STANCE_LOW_COVER);
  251.         m_dictStance.Add("HighCover", STANCE_HIGH_COVER);
  252.         m_dictStance.Add("Swim", STANCE_SWIM);
  253.         m_dictStance.Add("ZeroG", STANCE_ZEROG);
  254.  
  255.         m_dictUrgency.Add("Zero", AISPEED_ZERO);
  256.         m_dictUrgency.Add("Slow", AISPEED_SLOW);
  257.         m_dictUrgency.Add("Walk", AISPEED_WALK);
  258.         m_dictUrgency.Add("Run", AISPEED_RUN);
  259.         m_dictUrgency.Add("Sprint", AISPEED_SPRINT);
  260. }
  261.  
  262. bool CGoalOpXMLReader::GetBool(const XmlNodeRef& node, const char* szAttrName, bool bDefaultValue)
  263. {
  264.         bool bValue = false;
  265.         if (m_dictBools.Get(node, szAttrName, bValue))
  266.         {
  267.                 return bValue;
  268.         }
  269.  
  270.         return bDefaultValue;
  271. }
  272.  
  273. bool CGoalOpXMLReader::GetMandatoryBool(const XmlNodeRef& node, const char* szAttrName)
  274. {
  275.         bool bValue;
  276.         return m_dictBools.Get(node, szAttrName, bValue, true) ? bValue : false;
  277. }
  278.  
  279. const char* CGoalOpXMLReader::GetMandatoryString(const XmlNodeRef& node, const char* szAttrName)
  280. {
  281.         const char* sz;
  282.         if (!node->getAttr(szAttrName, &sz))
  283.         {
  284.                 AIError("Unable to get mandatory string attribute '%s' of node '%s'.",
  285.                         szAttrName, node->getTag());
  286.                 sz = "";
  287.         }
  288.  
  289.         return sz;
  290. }
  291.  
  292. //#pragma optimize("", off)
  293. //#pragma inline_depth(0)
  294.  
  295. // The end point accuracy to use when tracing and we don't really care. 0 results in exact positioning being used
  296. // a small value (0.1) allows some sloppiness. Both should work.
  297. static float defaultTraceEndAccuracy = 0.1f;
  298.  
  299. EGoalOpResult COPAcqTarget::Execute(CPipeUser* pPipeUser)
  300. {
  301.         CCCPOINT(COPAcqTarget_Execute);
  302.         FUNCTION_PROFILER(GetISystem(), PROFILE_AI);
  303.  
  304.         pPipeUser->m_bLooseAttention = false;
  305.  
  306.         CAIObject* pTargetObject = m_sTargetName.empty() ? 0 : pPipeUser->GetSpecialAIObject(m_sTargetName);
  307.  
  308.         pPipeUser->SetAttentionTarget(pTargetObject ? GetWeakRef(pTargetObject) : pPipeUser->m_refLastOpResult);
  309.  
  310.         return eGOR_DONE;
  311. }
  312.  
  313. COPApproach::COPApproach(float fEndDistance, float fEndAccuracy, float fDuration,
  314.                          bool bUseLastOpResult, bool bLookAtLastOp,
  315.                          bool bForceReturnPartialPath, bool bStopOnAnimationStart,
  316.                          const char* szNoPathSignalText) :
  317.         m_fLastDistance(0.0f),
  318.         m_fInitialDistance(0.0f),
  319.         m_bUseLastOpResult(bUseLastOpResult),
  320.         m_bLookAtLastOp(bLookAtLastOp),
  321.         m_fEndDistance(fEndDistance),
  322.         m_fEndAccuracy(fEndAccuracy),
  323.         m_fDuration(fDuration),
  324.         m_bForceReturnPartialPath(bForceReturnPartialPath),
  325.         m_stopOnAnimationStart(bStopOnAnimationStart),
  326.         m_looseAttentionId(0),
  327.         m_pTraceDirective(0),
  328.         m_pPathfindDirective(0),
  329.         m_bPathFound(false)
  330. {
  331.         CCCPOINT(COPApproach_COPApproach);
  332. }
  333.  
  334. COPApproach::COPApproach(const XmlNodeRef& node) :
  335.         m_fLastDistance(0.f),
  336.         m_fInitialDistance(0.f),
  337.         m_bUseLastOpResult(s_xml.GetBool(node, "useLastOp")),
  338.         m_bLookAtLastOp(s_xml.GetBool(node, "lookAtLastOp")),
  339.         m_fEndDistance(0.f),
  340.         m_fEndAccuracy(0.f),
  341.         m_fDuration(0.f),
  342.         m_bForceReturnPartialPath(s_xml.GetBool(node, "requestPartialPath")),
  343.         m_stopOnAnimationStart(s_xml.GetBool(node, "stopOnAnimationStart")),
  344.         m_looseAttentionId(0),
  345.         m_pTraceDirective(0),
  346.         m_pPathfindDirective(0),
  347.         m_bPathFound(false)
  348. {
  349.         if (node->getAttr("time", m_fDuration))
  350.         {
  351.                 m_fDuration = fabsf(m_fDuration);
  352.         }
  353.         else
  354.         {
  355.                 s_xml.GetMandatory(node, "distance", m_fEndDistance);
  356.         }
  357.  
  358.         node->getAttr("endAccuracy", m_fEndAccuracy);
  359. }
  360.  
  361. void COPApproach::DebugDraw(CPipeUser* pPipeUser) const
  362. {
  363.         // TODO evgeny Consider an abstract class to avoid retyping m_pTraceDirective/m_pPathfindDirective
  364.         if (m_pTraceDirective)
  365.         {
  366.                 m_pTraceDirective->DebugDraw(pPipeUser);
  367.         }
  368.  
  369.         if (m_pPathfindDirective)
  370.         {
  371.                 m_pPathfindDirective->DebugDraw(pPipeUser);
  372.         }
  373. }
  374.  
  375. EGoalOpResult COPApproach::Execute(CPipeUser* pPipeUser)
  376. {
  377.         CCCPOINT(COPApproach_Execute);
  378.         FUNCTION_PROFILER(GetISystem(), PROFILE_AI);
  379.  
  380.         int debugPathfinding = gAIEnv.CVars.DebugPathFinding;
  381.  
  382.         CAIObject* pTarget = static_cast<CAIObject*>(pPipeUser->GetAttentionTarget());
  383.  
  384.         // Move strictly to the target point
  385.         pPipeUser->m_bPathfinderConsidersPathTargetDirection = true;
  386.  
  387.         if (!pTarget || m_bUseLastOpResult)
  388.         {
  389.                 CAIObject* pLastOpResult = pPipeUser->m_refLastOpResult.GetAIObject();
  390.                 if (pLastOpResult)
  391.                 {
  392.                         pTarget = pLastOpResult;
  393.                 }
  394.                 else
  395.                 {
  396.                         // no target, nothing to approach to
  397.                         if (debugPathfinding)
  398.                                 AILogAlways("COPApproach::Execute %s no target; resetting goalop", GetNameSafe(pPipeUser));
  399.                         Reset(pPipeUser);
  400.                         return eGOR_FAILED;
  401.                 }
  402.         }
  403.  
  404.         // luciano - added check for formation points
  405.         if (pTarget && !pTarget->IsEnabled())
  406.         {
  407.                 if (debugPathfinding)
  408.                         AILogAlways("COPApproach::Execute %s target %s not enabled", GetNameSafe(pPipeUser), pTarget->GetName());
  409.                 Reset(pPipeUser);
  410.                 return eGOR_FAILED;
  411.         }
  412.  
  413.         Vec3 mypos = pPipeUser->GetPos();
  414.  
  415.         Vec3 targetpos;
  416.         Vec3 targetdir;
  417.         if ((pPipeUser->m_nPathDecision == PATHFINDER_PATHFOUND) && m_bForceReturnPartialPath)
  418.         {
  419.                 targetpos = pPipeUser->m_Path.GetLastPathPos();
  420.                 targetdir = pPipeUser->m_Path.GetEndDir();
  421.         }
  422.         else
  423.         {
  424.                 targetpos = pTarget->GetPhysicsPos();
  425.                 targetdir = pTarget->GetMoveDir();
  426.         }
  427.  
  428.         CWeakRef<CAIObject>& refLastOpResult = pPipeUser->m_refLastOpResult;
  429.         if (!m_looseAttentionId && m_bLookAtLastOp && refLastOpResult.IsValid())
  430.         {
  431.                 m_looseAttentionId = pPipeUser->SetLooseAttentionTarget(refLastOpResult);
  432.         }
  433.  
  434.         if (pPipeUser->GetSubType() == CAIObject::STP_HELI)
  435.         {
  436.                 // Make sure helicopter gets up first, and then starts moving
  437.                 if (CAIVehicle* pHeli = pPipeUser->CastToCAIVehicle())
  438.                         if (pHeli->HandleVerticalMovement(targetpos))
  439.                                 return eGOR_IN_PROGRESS;
  440.         }
  441.  
  442.         if (!pPipeUser->m_movementAbility.bUsePathfinder)
  443.         {
  444.                 Vec3 projectedDist = mypos - targetpos;
  445.                 float dist = pPipeUser->m_movementAbility.b3DMove ? projectedDist.GetLength() : projectedDist.GetLength2D();
  446.  
  447.                 float endDistance = GetEndDistance(pPipeUser);
  448.  
  449.                 if (dist < endDistance)
  450.                 {
  451.                         if (debugPathfinding)
  452.                                 AILogAlways("COPApproach::Execute %s No pathfinder and reached end", GetNameSafe(pPipeUser));
  453.                         m_fInitialDistance = 0;
  454.                         Reset(pPipeUser);
  455.                         return eGOR_SUCCEEDED;
  456.                 }
  457.  
  458.                 // no pathfinding - just approach
  459.                 pPipeUser->m_State.vMoveDir = targetpos - pPipeUser->GetPhysicsPos();
  460.                 pPipeUser->m_State.vMoveDir.Normalize();
  461.  
  462. #ifdef _DEBUG
  463.                 // Update the debug movement reason.
  464.                 pPipeUser->m_DEBUGmovementReason = CPipeUser::AIMORE_SMARTOBJECT;
  465. #endif
  466.  
  467.                 m_fLastDistance = dist;
  468.                 return eGOR_IN_PROGRESS;
  469.         }
  470.  
  471.         if (!m_bPathFound && !m_pPathfindDirective)
  472.         {
  473.                 // generate path to target
  474.                 float endTol = m_bForceReturnPartialPath || m_fEndAccuracy < 0.0f ? std::numeric_limits<float>::max() : m_fEndAccuracy;
  475.  
  476.                 // override end distance if a duration has been set
  477.                 float endDistance = GetEndDistance(pPipeUser);
  478.                 m_pPathfindDirective = new COPPathFind("", pTarget, endTol, endDistance);
  479.                 pPipeUser->m_nPathDecision = PATHFINDER_STILLFINDING;
  480.  
  481.                 if (m_pPathfindDirective->Execute(pPipeUser) != eGOR_IN_PROGRESS)
  482.                 {
  483.                         if (pPipeUser->m_nPathDecision == PATHFINDER_NOPATH)
  484.                         {
  485.                                 if (debugPathfinding)
  486.                                         AILogAlways("COPApproach::Execute %s pathfinder no path", GetNameSafe(pPipeUser));
  487.                                 // If special nopath signal is specified, send the signal.
  488.                                 if (m_noPathSignalText.size() > 0)
  489.                                         pPipeUser->SetSignal(0, m_noPathSignalText.c_str(), NULL);
  490.                                 pPipeUser->m_State.vMoveDir.zero();
  491.                                 Reset(pPipeUser);
  492.                                 return eGOR_FAILED;
  493.                         }
  494.                 }
  495.                 return eGOR_IN_PROGRESS;
  496.         }
  497.  
  498.         // trace/pathfinding gets deleted when we reach the end
  499.         if (!m_pPathfindDirective)
  500.         {
  501.                 if (debugPathfinding)
  502.                 {
  503.                         AILogAlways("COPApproach::Execute (%p) returning true due to no pathfinding directive %s", this, GetNameSafe(pPipeUser));
  504.                 }
  505.                 Reset(pPipeUser);
  506.                 return eGOR_FAILED;
  507.         }
  508.  
  509.         // actually trace the path - continue doing this even whilst regenerating (etc) the path
  510.         EGoalOpResult doneTracing = eGOR_IN_PROGRESS;
  511.         if (m_bPathFound)
  512.         {
  513.                 if (!m_pTraceDirective)
  514.                 {
  515.                         if ((pTarget->GetSubType() == CAIObject::STP_ANIM_TARGET) && (m_fEndDistance > 0.0f || m_fDuration > 0.0f))
  516.                         {
  517.                                 AILogAlways("COPApproach::Execute resetting approach distance from (endDist=%.1f duration=%.1f) to zero because the approach target is anim target. %s",
  518.                                             m_fEndDistance, m_fDuration, GetNameSafe(pPipeUser));
  519.                                 m_fEndDistance = 0.0f;
  520.                                 m_fDuration = 0.0f;
  521.                         }
  522.  
  523.                         TPathPoints::const_reference lastPathNode = pPipeUser->m_OrigPath.GetPath().back();
  524.                         Vec3 lastPos = lastPathNode.vPos;
  525.                         Vec3 requestedLastNodePos = pPipeUser->m_Path.GetParams().end;
  526.                         float dist = Distance::Point_Point(lastPos, requestedLastNodePos);
  527.                         float endDistance = GetEndDistance(pPipeUser);
  528.                         if (lastPathNode.navType != IAISystem::NAV_SMARTOBJECT && dist > endDistance + C_MaxDistanceForPathOffset)// && pPipeUser->m_Path.GetPath().size() == 1 )
  529.                         {
  530.                                 AISignalExtraData* pData = new AISignalExtraData;
  531.                                 pData->fValue = dist - endDistance;
  532.                                 pPipeUser->SetSignal(0, "OnEndPathOffset", pPipeUser->GetEntity(), pData, gAIEnv.SignalCRCs.m_nOnEndPathOffset);
  533.                         }
  534.                         else
  535.                         {
  536.                                 pPipeUser->SetSignal(0, "OnPathFound", NULL, 0, gAIEnv.SignalCRCs.m_nOnPathFound);
  537.                         }
  538.  
  539.                         bool bExact = false;
  540.                         //      m_pTraceDirective = new COPTrace(bExact, endDistance, m_fEndAccuracy, m_fDuration, m_bForceReturnPartialPath, m_stopOnAnimationStart);
  541.                         m_pTraceDirective = new COPTrace(bExact, m_fEndAccuracy, m_bForceReturnPartialPath, m_stopOnAnimationStart);
  542.                 }
  543.  
  544.                 doneTracing = m_pTraceDirective->Execute(pPipeUser);
  545.  
  546.                 // If this goal gets reseted during m_pTraceDirective->Execute it means that
  547.                 // a smart object was used for navigation which inserts a goal pipe which
  548.                 // does Reset on this goal which sets m_pTraceDirective to NULL! In this case
  549.                 // we should just report that this goal pipe isn't finished since it will be
  550.                 // reexecuted after finishing the inserted goal pipe
  551.                 if (!m_pTraceDirective)
  552.                         return eGOR_IN_PROGRESS;
  553.  
  554.                 // If the path has been traced, finish the operation
  555.                 if (doneTracing != eGOR_IN_PROGRESS)
  556.                 {
  557.                         if (debugPathfinding)
  558.                                 AILogAlways("COPApproach::Execute (%p) finishing due to finished tracing %s", this, GetNameSafe(pPipeUser));
  559.                         Reset(pPipeUser);
  560.                         return doneTracing;
  561.                 }
  562.         }
  563.  
  564.         // check pathfinder status
  565.         switch (pPipeUser->m_nPathDecision)
  566.         {
  567.         case PATHFINDER_STILLFINDING:
  568.                 m_pPathfindDirective->Execute(pPipeUser);
  569.                 return eGOR_IN_PROGRESS;
  570.  
  571.         case PATHFINDER_NOPATH:
  572.                 pPipeUser->m_State.vMoveDir.zero();
  573.                 if (debugPathfinding)
  574.                         AILogAlways("COPApproach::Execute (%p) resetting due to no path %s", this, GetNameSafe(pPipeUser));
  575.                 // If special nopath signal is specified, send the signal.
  576.                 if (m_noPathSignalText.size() > 0)
  577.                         pPipeUser->SetSignal(0, m_noPathSignalText.c_str(), NULL);
  578.                 Reset(pPipeUser);
  579.                 return eGOR_FAILED;
  580.  
  581.         case PATHFINDER_PATHFOUND:
  582.                 if (!m_bPathFound)
  583.                 {
  584.                         m_bPathFound = true;
  585.                         return Execute(pPipeUser);
  586.                 }
  587.                 break;
  588.         }
  589.  
  590.         return eGOR_IN_PROGRESS;
  591. }
  592.  
  593. void COPApproach::Reset(CPipeUser* pPipeUser)
  594. {
  595.         if (gAIEnv.CVars.DebugPathFinding)
  596.         {
  597.                 AILogAlways("COPApproach::Reset %s", GetNameSafe(pPipeUser));
  598.         }
  599.  
  600.         SAFE_DELETE(m_pPathfindDirective);
  601.         SAFE_DELETE(m_pTraceDirective);
  602.  
  603.         m_bPathFound = false;
  604.  
  605.         m_fInitialDistance = 0;
  606.  
  607.         if (pPipeUser)
  608.         {
  609.                 pPipeUser->ClearPath("COPApproach::Reset m_Path");
  610.                 if (m_bLookAtLastOp)
  611.                 {
  612.                         pPipeUser->SetLooseAttentionTarget(NILREF, m_looseAttentionId);
  613.                         m_looseAttentionId = 0;
  614.                 }
  615.         }
  616. }
  617.  
  618. void COPApproach::Serialize(TSerialize ser)
  619. {
  620.         ser.BeginGroup("COPApproach");
  621.         {
  622.                 ser.Value("m_fLastDistance", m_fLastDistance);
  623.                 ser.Value("m_fInitialDistance", m_fInitialDistance);
  624.                 ser.Value("m_bUseLastOpResult", m_bUseLastOpResult);
  625.                 ser.Value("m_bLookAtLastOp", m_bLookAtLastOp);
  626.                 ser.Value("m_fEndDistance", m_fEndDistance);
  627.                 ser.Value("m_fEndAccuracy", m_fEndAccuracy);
  628.                 ser.Value("m_fDuration", m_fDuration);
  629.                 ser.Value("m_bForceReturnPartialPath", m_bForceReturnPartialPath);
  630.                 ser.Value("m_stopOnAnimationStart", m_stopOnAnimationStart);
  631.                 ser.Value("m_noPathSignalText", m_noPathSignalText);
  632.                 ser.Value("m_looseAttentionId", m_looseAttentionId);
  633.                 ser.Value("m_bPathFound", m_bPathFound);
  634.  
  635.                 if (ser.IsWriting())
  636.                 {
  637.                         if (ser.BeginOptionalGroup("TraceDirective", m_pTraceDirective != NULL))
  638.                         {
  639.                                 PREFAST_SUPPRESS_WARNING(6011) m_pTraceDirective->Serialize(ser);
  640.                                 ser.EndGroup();
  641.                         }
  642.                         if (ser.BeginOptionalGroup("PathFindDirective", m_pPathfindDirective != NULL))
  643.                         {
  644.                                 PREFAST_SUPPRESS_WARNING(6011) m_pPathfindDirective->Serialize(ser);
  645.                                 ser.EndGroup();
  646.                         }
  647.                 }
  648.                 else
  649.                 {
  650.                         SAFE_DELETE(m_pTraceDirective);
  651.                         if (ser.BeginOptionalGroup("TraceDirective", true))
  652.                         {
  653.                                 m_pTraceDirective = new COPTrace(true);
  654.                                 m_pTraceDirective->Serialize(ser);
  655.                                 ser.EndGroup();
  656.                         }
  657.                         SAFE_DELETE(m_pPathfindDirective);
  658.                         if (ser.BeginOptionalGroup("PathFindDirective", true))
  659.                         {
  660.                                 m_pPathfindDirective = new COPPathFind("");
  661.                                 m_pPathfindDirective->Serialize(ser);
  662.                                 ser.EndGroup();
  663.                         }
  664.                 }
  665.         }
  666.         ser.EndGroup();
  667. }
  668.  
  669. //===================================================================
  670. // GetEndDistance
  671. //===================================================================
  672. float COPApproach::GetEndDistance(CPipeUser* pPipeUser) const
  673. {
  674.         if (m_fDuration > 0.0f)
  675.         {
  676.                 //              float normalSpeed = pPipeUser->GetNormalMovementSpeed(pPipeUser->m_State.fMovementUrgency, false);
  677.                 float normalSpeed, smin, smax;
  678.                 pPipeUser->GetMovementSpeedRange(pPipeUser->m_State.fMovementUrgency, false, normalSpeed, smin, smax);
  679.                 if (normalSpeed > 0.0f)
  680.                         return -normalSpeed * m_fDuration;
  681.         }
  682.         return m_fEndDistance;
  683. }
  684.  
  685. //===================================================================
  686. // ExecuteDry
  687. //===================================================================
  688. void COPApproach::ExecuteDry(CPipeUser* pPipeUser)
  689. {
  690.         FUNCTION_PROFILER(GetISystem(), PROFILE_AI);
  691.         if (m_pTraceDirective && m_bPathFound)
  692.                 m_pTraceDirective->ExecuteTrace(pPipeUser, false);
  693. }
  694.  
  695. // COPFollowPath
  696. //====================================================================
  697. COPFollowPath::COPFollowPath(bool pathFindToStart, bool reverse, bool startNearest, int loops, float fEndAccuracy, bool bUsePointList, bool bControlSpeed, float desiredSpeedOnPath) :
  698.         m_pathFindToStart(pathFindToStart),
  699.         m_reversePath(reverse),
  700.         m_startNearest(startNearest),
  701.         m_bUsePointList(bUsePointList),
  702.         m_bControlSpeed(bControlSpeed),
  703.         m_loops(loops),
  704.         m_loopCounter(0),
  705.         m_notMovingTimeMs(0),
  706.         m_returningToPath(false),
  707.         m_fDesiredSpeedOnPath(desiredSpeedOnPath)
  708. {
  709.         m_pTraceDirective = 0;
  710.         m_pPathFindDirective = 0;
  711.         m_fEndAccuracy = fEndAccuracy;
  712. }
  713.  
  714. COPFollowPath::COPFollowPath(const XmlNodeRef& node) :
  715.         m_pathFindToStart(s_xml.GetBool(node, "pathFindToStart", true)),
  716.         m_reversePath(s_xml.GetBool(node, "reversePath", true)),
  717.         m_startNearest(s_xml.GetBool(node, "startNearest", true)),
  718.         m_bUsePointList(s_xml.GetBool(node, "usePointList")),
  719.         m_loops(0),
  720.         m_loopCounter(0),
  721.         m_notMovingTimeMs(0),
  722.         m_returningToPath(false),
  723.         m_pTraceDirective(0),
  724.         m_pPathFindDirective(0),
  725.         m_fEndAccuracy(0),
  726.         m_bControlSpeed(true),
  727.         m_fDesiredSpeedOnPath(0.0f)
  728. {
  729.         s_xml.GetMandatory(node, "loops", m_loops);
  730.         node->getAttr("endAccuracy", m_fEndAccuracy);
  731.  
  732.         s_xml.GetMandatory(node, "speed", m_fDesiredSpeedOnPath);
  733. }
  734.  
  735. COPFollowPath::COPFollowPath(const COPFollowPath& rhs)
  736.         : m_pTraceDirective(nullptr)
  737.         , m_pPathFindDirective(nullptr)
  738.         , m_refPathStartPoint()
  739.         , m_pathFindToStart(rhs.m_pathFindToStart)
  740.         , m_reversePath(rhs.m_reversePath)
  741.         , m_startNearest(rhs.m_startNearest)
  742.         , m_bUsePointList(rhs.m_bUsePointList)
  743.         , m_bControlSpeed(rhs.m_bControlSpeed)
  744.         , m_loops(rhs.m_loops)
  745.         , m_loopCounter(0)
  746.         , m_notMovingTimeMs(0)
  747.         , m_returningToPath(false)
  748.         , m_fEndAccuracy(rhs.m_fEndAccuracy)
  749.         , m_fDesiredSpeedOnPath(rhs.m_fDesiredSpeedOnPath)
  750. {
  751. }
  752.  
  753. //====================================================================
  754. // Reset
  755. //====================================================================
  756. void COPFollowPath::Reset(CPipeUser* pPipeUser)
  757. {
  758.         CCCPOINT(COPFollowPath_Reset);
  759.  
  760.         // (MATT) Apparently we hang onto the path start dummy {2009/02/17}
  761.         delete m_pTraceDirective;
  762.         m_pTraceDirective = 0;
  763.         delete m_pPathFindDirective;
  764.         m_pPathFindDirective = 0;
  765.         m_loopCounter = 0;
  766.         m_notMovingTimeMs = 0;
  767.         m_returningToPath = false;
  768.         m_fEndAccuracy = defaultTraceEndAccuracy;
  769.         m_fDesiredSpeedOnPath = 0.0f;
  770.         if (pPipeUser)
  771.                 pPipeUser->ClearPath("COPFollowPath::Reset m_Path");
  772. }
  773.  
  774. //====================================================================
  775. // Serialize
  776. //====================================================================
  777. void COPFollowPath::Serialize(TSerialize ser)
  778. {
  779.         ser.BeginGroup("COPFollowPath");
  780.         {
  781.                 ser.Value("m_pathFindToStart", m_pathFindToStart);
  782.                 ser.Value("m_reversePath", m_reversePath);
  783.                 ser.Value("m_startNearest", m_startNearest);
  784.                 ser.Value("m_loops", m_loops);
  785.                 ser.Value("m_loopCounter", m_loopCounter);
  786.                 ser.Value("m_TraceEndAccuracy", m_fEndAccuracy);
  787.                 ser.Value("m_fDesiredSpeedOnPath", m_fDesiredSpeedOnPath);
  788.                 ser.Value("m_notMovingTimeMs", m_notMovingTimeMs);
  789.                 ser.Value("m_returningToPath", m_returningToPath);
  790.                 ser.Value("m_bUsePointList", m_bUsePointList);
  791.                 ser.Value("m_bControlSpeed", m_bControlSpeed);
  792.  
  793.                 m_refPathStartPoint.Serialize(ser, "m_refPathStartPoint");
  794.  
  795.                 if (ser.IsWriting())
  796.                 {
  797.                         if (ser.BeginOptionalGroup("TraceDirective", m_pTraceDirective != NULL))
  798.                         {
  799.                                 PREFAST_SUPPRESS_WARNING(6011) m_pTraceDirective->Serialize(ser);
  800.                                 ser.EndGroup();
  801.                         }
  802.                         if (ser.BeginOptionalGroup("PathFindDirective", m_pPathFindDirective != NULL))
  803.                         {
  804.                                 PREFAST_SUPPRESS_WARNING(6011) m_pPathFindDirective->Serialize(ser);
  805.                                 ser.EndGroup();
  806.                         }
  807.                 }
  808.                 else
  809.                 {
  810.                         SAFE_DELETE(m_pTraceDirective);
  811.                         if (ser.BeginOptionalGroup("TraceDirective", true))
  812.                         {
  813.                                 m_pTraceDirective = new COPTrace(false, m_fEndAccuracy);
  814.                                 m_pTraceDirective->Serialize(ser);
  815.                                 m_pTraceDirective->SetControlSpeed(m_bControlSpeed);
  816.                                 ser.EndGroup();
  817.                         }
  818.                         SAFE_DELETE(m_pPathFindDirective);
  819.                         if (ser.BeginOptionalGroup("PathFindDirective", true))
  820.                         {
  821.                                 m_pPathFindDirective = new COPPathFind("");
  822.                                 m_pPathFindDirective->Serialize(ser);
  823.                                 ser.EndGroup();
  824.                         }
  825.                 }
  826.                 ser.EndGroup();
  827.         }
  828. }
  829.  
  830. //====================================================================
  831. // Execute
  832. //====================================================================
  833. EGoalOpResult COPFollowPath::Execute(CPipeUser* pPipeUser)
  834. {
  835.         FUNCTION_PROFILER(GetISystem(), PROFILE_AI);
  836.         CAIObject* pTarget = (CAIObject*)pPipeUser->GetAttentionTarget();
  837.         CAISystem* pSystem = GetAISystem();
  838.  
  839.         // for a temporary, until all functionality have made.
  840.         if (m_bUsePointList == true && !(m_pathFindToStart == false && m_reversePath == false && m_startNearest == false))
  841.         {
  842.                 AIWarning("COPFollowPath:: bUsePointList only support false,false,false in first 3 parameters for %s", GetNameSafe(pPipeUser));
  843.                 return eGOR_FAILED;
  844.         }
  845.  
  846.         CCCPOINT(COPFollowPath_Execute);
  847.  
  848.         // if we have a path, trace it. Once we get going (i.e. pathfind is finished, if we use it) we'll
  849.         // always have a trace
  850.         if (!m_pTraceDirective)
  851.         {
  852.                 if (m_pPathFindDirective)
  853.                 {
  854.                         CCCPOINT(COPFollowPath_Execute_A);
  855.  
  856.                         EGoalOpResult finishedPathFind = m_pPathFindDirective->Execute(pPipeUser);
  857.                         if (finishedPathFind != eGOR_IN_PROGRESS)
  858.                         {
  859.                                 pPipeUser->m_State.fDesiredSpeed = m_fDesiredSpeedOnPath;
  860.                                 m_pTraceDirective = new COPTrace(false, m_fEndAccuracy);
  861.                                 if (!m_bControlSpeed)
  862.                                         m_pTraceDirective->SetControlSpeed(false);
  863.  
  864.                                 m_lastTime = pSystem->GetFrameStartTime();
  865.                         }
  866.                         else
  867.                                 return eGOR_IN_PROGRESS;
  868.                 }
  869.                 else // no pathfind directive
  870.                 {
  871.                         //      m_pathFindToStart = pPipeUser->GetPathFindToStartOfPathToFollow(pathStartPos);
  872.                         if (m_pathFindToStart)
  873.                         {
  874.                                 CCCPOINT(COPFollowPath_Execute_B);
  875.  
  876.                                 // Create the PathStartPoint if needed
  877.                                 if (m_refPathStartPoint.IsNil() || !m_refPathStartPoint->GetAIObjectID())
  878.                                         gAIEnv.pAIObjectManager->CreateDummyObject(m_refPathStartPoint, string(GetNameSafe(pPipeUser)) + "_PathStartPoint", CAIObject::STP_REFPOINT);
  879.  
  880.                                 Vec3 entryPos;
  881.                                 if (!pPipeUser->GetPathEntryPoint(entryPos, m_reversePath, m_startNearest))
  882.                                 {
  883.                                         AIWarning("COPFollowPath::Unable to find path entry point for %s - check path is not marked as a road and that all AI has been generated and exported", GetNameSafe(pPipeUser));
  884.                                         return eGOR_FAILED;
  885.                                 }
  886.                                 m_refPathStartPoint->SetPos(entryPos);
  887.                                 m_pPathFindDirective = new COPPathFind("FollowPath", m_refPathStartPoint.GetAIObject());
  888.  
  889.                                 return eGOR_IN_PROGRESS;
  890.                         }
  891.                         else
  892.                         {
  893.                                 CCCPOINT(COPFollowPath_Execute_C);
  894.  
  895.                                 if (m_bUsePointList == true)
  896.                                 {
  897.                                         bool pathOK = pPipeUser->UsePointListToFollow();
  898.                                         if (!pathOK)
  899.                                         {
  900.                                                 AIWarning("COPFollowPath::Execute Unable to use point list %s", GetNameSafe(pPipeUser));
  901.                                                 return eGOR_FAILED;
  902.                                         }
  903.                                 }
  904.                                 else
  905.                                 {
  906.                                         bool pathOK = pPipeUser->UsePathToFollow(m_reversePath, m_startNearest, m_loops != 0);
  907.                                         if (!pathOK)
  908.                                         {
  909.                                                 AIWarning("COPFollowPath::Execute Unable to use follow path for %s - check path is not marked as a road and that all AI has been generated and exported", GetNameSafe(pPipeUser));
  910.                                                 return eGOR_FAILED;
  911.                                         }
  912.                                 }
  913.                                 pPipeUser->m_State.fDesiredSpeed = m_fDesiredSpeedOnPath;
  914.                                 m_pTraceDirective = new COPTrace(false, m_fEndAccuracy);
  915.                                 if (!m_bControlSpeed)
  916.                                         m_pTraceDirective->SetControlSpeed(false);
  917.  
  918.                                 m_lastTime = pSystem->GetFrameStartTime();
  919.                         } // m_pathFindToStart
  920.                 }   // path find directive
  921.         }
  922.         AIAssert(m_pTraceDirective);
  923.         EGoalOpResult done = m_pTraceDirective->Execute(pPipeUser);
  924.  
  925.         // HACK: The following code tries to put a lost or stuck agent back on track.
  926.         // It works together with a piece of in ExecuteDry which tracks the speed relative
  927.         // to the requested speed and if it drops dramatically for certain time, this code
  928.         // will trigger and try to move the agent back on the path. [Mikko]
  929.  
  930.         int timeout = 700;
  931.         if (pPipeUser->GetType() == AIOBJECT_VEHICLE)
  932.                 timeout = 7000;
  933.  
  934.         if (pPipeUser->GetSubType() == CAIObject::STP_2D_FLY)
  935.                 m_notMovingTimeMs = 0;
  936.  
  937.         if (m_notMovingTimeMs > timeout)
  938.         {
  939.                 CCCPOINT(COPFollowPath_Execute_Stuck);
  940.  
  941.                 // Stuck or lost, move to the nearest point on path.
  942.                 AIWarning("COPFollowPath::Entity %s has not been moving fast enough for %.1fs it might be stuck, find back to path.", GetNameSafe(pPipeUser), m_notMovingTimeMs * 0.001f);
  943.  
  944.                 // Create the PathStartPoint if needed
  945.                 // (MATT) And if definately is, sometimes. But fix this code duplication. {2009/02/18}
  946.                 if (m_refPathStartPoint.IsNil() || !m_refPathStartPoint->GetAIObjectID())
  947.                         gAIEnv.pAIObjectManager->CreateDummyObject(m_refPathStartPoint, string(GetNameSafe(pPipeUser)) + "_PathStartPoint", CAIObject::STP_REFPOINT);
  948.  
  949.                 Vec3 entryPos;
  950.                 if (!pPipeUser->GetPathEntryPoint(entryPos, m_reversePath, true))
  951.                 {
  952.                         AIWarning("COPFollowPath::Unable to find path entry point for %s - check path is not marked as a road and that all AI has been generated and exported", GetNameSafe(pPipeUser));
  953.                         return eGOR_FAILED;
  954.                 }
  955.                 m_refPathStartPoint->SetPos(entryPos);
  956.                 m_pPathFindDirective = new COPPathFind("FollowPath", m_refPathStartPoint.GetAIObject());
  957.                 delete m_pTraceDirective;
  958.                 m_pTraceDirective = 0;
  959.                 m_notMovingTimeMs = 0;
  960.                 m_returningToPath = true;
  961.         }
  962.  
  963.         if (done != eGOR_IN_PROGRESS || !m_pTraceDirective)
  964.         {
  965.                 if ((m_pathFindToStart || m_returningToPath) && m_pPathFindDirective)
  966.                 {
  967.                         CCCPOINT(COPFollowPath_Execute_D);
  968.  
  969.                         pPipeUser->SetSignal(1, "OnPathFindAtStart", 0, 0, gAIEnv.SignalCRCs.m_nOnPathFindAtStart);
  970.  
  971.                         delete m_pPathFindDirective;
  972.                         m_pPathFindDirective = 0;
  973.                         delete m_pTraceDirective;
  974.                         bool pathOK = pPipeUser->UsePathToFollow(m_reversePath, (m_startNearest || m_returningToPath), false);
  975.                         if (!pathOK)
  976.                         {
  977.                                 AIWarning("COPFollowPath::Execute Unable to use follow path for %s - check path is not marked as a road and that all AI has been generated and exported", GetNameSafe(pPipeUser));
  978.                                 return eGOR_FAILED;
  979.                         }
  980.                         pPipeUser->m_State.fDesiredSpeed = m_fDesiredSpeedOnPath;
  981.                         m_pTraceDirective = new COPTrace(false, m_fEndAccuracy);
  982.                         if (!m_bControlSpeed)
  983.                                 m_pTraceDirective->SetControlSpeed(false);
  984.  
  985.                         m_lastTime = pSystem->GetFrameStartTime();
  986.                         m_returningToPath = false;
  987.                         return eGOR_IN_PROGRESS;
  988.                 }
  989.                 else
  990.                 {
  991.                         if (m_loops != 0)
  992.                         {
  993.                                 CCCPOINT(COPFollowPath_Execute_E);
  994.  
  995.                                 m_loopCounter++;
  996.                                 if (m_loops != -1 && m_loopCounter >= m_loops)
  997.                                 {
  998.                                         Reset(pPipeUser);
  999.                                         return eGOR_SUCCEEDED;
  1000.                                 }
  1001.  
  1002.                                 delete m_pTraceDirective;
  1003.                                 bool pathOK = pPipeUser->UsePathToFollow(m_reversePath, m_startNearest, m_loops != 0);
  1004.                                 if (!pathOK)
  1005.                                 {
  1006.                                         AIWarning("COPFollowPath::Execute Unable to use follow path for %s - check path is not marked as a road and that all AI has been generated and exported", GetNameSafe(pPipeUser));
  1007.                                         return eGOR_FAILED;
  1008.                                 }
  1009.  
  1010.                                 pPipeUser->m_State.fDesiredSpeed = m_fDesiredSpeedOnPath;
  1011.  
  1012.                                 m_pTraceDirective = new COPTrace(false, m_fEndAccuracy);
  1013.                                 if (!m_bControlSpeed)
  1014.                                         m_pTraceDirective->SetControlSpeed(false);
  1015.                                 m_lastTime = pSystem->GetFrameStartTime();
  1016.                                 // For seamless operation, update trace already.
  1017.                                 m_pTraceDirective->Execute(pPipeUser);
  1018.                                 return eGOR_IN_PROGRESS;
  1019.                         }
  1020.                 }
  1021.  
  1022.                 Reset(pPipeUser);
  1023.                 return eGOR_SUCCEEDED;
  1024.         }
  1025.  
  1026.         return eGOR_IN_PROGRESS;
  1027. }
  1028.  
  1029. //====================================================================
  1030. // ExecuteDry
  1031. //====================================================================
  1032. void COPFollowPath::ExecuteDry(CPipeUser* pPipeUser)
  1033. {
  1034.         FUNCTION_PROFILER(GetISystem(), PROFILE_AI);
  1035.         CAISystem* pSystem = GetAISystem();
  1036.  
  1037.         if (m_pTraceDirective)
  1038.         {
  1039.                 CCCPOINT(COPFollowPath_ExecuteDry);
  1040.  
  1041.                 m_pTraceDirective->ExecuteTrace(pPipeUser, false);
  1042.  
  1043.                 // HACK: The following code together with some logic in the execute tries to keep track
  1044.                 // if the agent is not moving for some time (is stuck), and pathfinds back to the path. [Mikko]
  1045.                 CTimeValue time(pSystem->GetFrameStartTime());
  1046.                 int64 dt((time - m_lastTime).GetMilliSecondsAsInt64());
  1047.  
  1048.                 float speed = pPipeUser->GetVelocity().GetLength();
  1049.                 float desiredSpeed = pPipeUser->m_State.fDesiredSpeed;
  1050.  
  1051.                 if (desiredSpeed > 0.0f)
  1052.                 {
  1053.                         float ratio = clamp_tpl(speed / desiredSpeed, 0.0f, 1.0f);
  1054.                         if (ratio < 0.1f)
  1055.                                 m_notMovingTimeMs += (int)dt;
  1056.                         else
  1057.                                 m_notMovingTimeMs -= (int)dt;
  1058.  
  1059.                         if (m_notMovingTimeMs < 0)
  1060.                                 m_notMovingTimeMs = 0;
  1061.                 }
  1062.                 m_lastTime = time;
  1063.         }
  1064. }
  1065.  
  1066. //===================================================================
  1067. // COPBackoff
  1068. //===================================================================
  1069. COPBackoff::COPBackoff(float distance, float duration, int filter, float minDistance)
  1070. {
  1071.         m_fDistance = distance;
  1072.         m_fDuration = duration;
  1073.         m_iDirection = filter & (AI_MOVE_FORWARD | AI_MOVE_BACKWARD | AI_MOVE_LEFT | AI_MOVE_RIGHT | AI_MOVE_TOWARDS_GROUP | AI_MOVE_BACKLEFT | AI_MOVE_BACKRIGHT);
  1074.         m_fMinDistance = minDistance;
  1075.         if (m_iDirection == 0)
  1076.                 m_iDirection = AI_MOVE_BACKWARD;
  1077.  
  1078.         //m_iAvailableDirections = m_iDirection;
  1079.  
  1080.         m_bUseLastOp = (filter & AILASTOPRES_USE) != 0;
  1081.         m_bLookForward = (filter & AI_LOOK_FORWARD) != 0;
  1082.         m_bUseTargetPosition = (filter & AI_BACKOFF_FROM_TARGET) != 0;
  1083.         m_bRandomOrder = (filter & AI_RANDOM_ORDER) != 0;
  1084.         m_bCheckSlopeDistance = (filter & AI_CHECK_SLOPE_DISTANCE) != 0;
  1085.  
  1086.         if (m_fDistance < 0.f)
  1087.         {
  1088.                 m_fDistance = -m_fDistance;
  1089.         }
  1090.         m_pPathfindDirective = 0;
  1091.         m_pTraceDirective = 0;
  1092.         m_currentDirMask = AI_MOVE_BACKWARD;
  1093.         m_vBestFailedBackoffPos.zero();
  1094.         m_fMaxDistanceFound = 0.f;
  1095.         m_bTryingLessThanMinDistance = false;
  1096.         m_looseAttentionId = 0;
  1097.         ResetMoveDirections();
  1098. }
  1099.  
  1100. COPBackoff::COPBackoff(const XmlNodeRef& node) :
  1101.         m_fDistance(0.f),
  1102.         m_fDuration(0.f),
  1103.         m_fMinDistance(0.f),
  1104.         m_pPathfindDirective(0),
  1105.         m_pTraceDirective(0),
  1106.         m_currentDirMask(AI_MOVE_BACKWARD),
  1107.         m_vBestFailedBackoffPos(ZERO),
  1108.         m_fMaxDistanceFound(0.f),
  1109.         m_bTryingLessThanMinDistance(false),
  1110.         m_looseAttentionId(0),
  1111.         m_bUseLastOp(s_xml.GetBool(node, "useLastOp")),
  1112.         m_bLookForward(s_xml.GetBool(node, "lookForward")),
  1113.         m_bUseTargetPosition(s_xml.GetBool(node, "backOffFromTarget")),
  1114.         m_bRandomOrder(s_xml.GetBool(node, "randomOrder")),
  1115.         m_bCheckSlopeDistance(s_xml.GetBool(node, "checkSlopeDistance"))
  1116. {
  1117.         s_xml.GetMandatory(node, "distance", m_fDistance);
  1118.         if (m_fDistance < 0.f)
  1119.         {
  1120.                 m_fDistance = -m_fDistance;
  1121.         }
  1122.  
  1123.         node->getAttr("maxDuration", m_fDuration);
  1124.  
  1125.         m_iDirection = (s_xml.GetBool(node, "moveForward") ? AI_MOVE_FORWARD : 0) |
  1126.                        (s_xml.GetBool(node, "moveBackward") ? AI_MOVE_BACKWARD : 0) |
  1127.                        (s_xml.GetBool(node, "moveLeft") ? AI_MOVE_LEFT : 0) |
  1128.                        (s_xml.GetBool(node, "moveRight") ? AI_MOVE_RIGHT : 0) |
  1129.                        (s_xml.GetBool(node, "moveBackLeft") ? AI_MOVE_BACKLEFT : 0) |
  1130.                        (s_xml.GetBool(node, "moveBackRight") ? AI_MOVE_BACKRIGHT : 0) |
  1131.                        (s_xml.GetBool(node, "moveTowardsGroup") ? AI_MOVE_TOWARDS_GROUP : 0);
  1132.         if (m_iDirection == 0)
  1133.         {
  1134.                 m_iDirection = AI_MOVE_BACKWARD;
  1135.         }
  1136.  
  1137.         node->getAttr("minDistance", m_fMinDistance);
  1138.  
  1139.         ResetMoveDirections();
  1140. }
  1141.  
  1142. COPBackoff::COPBackoff(const COPBackoff& rhs)
  1143.         : m_bUseTargetPosition(rhs.m_bUseTargetPosition)
  1144.         , m_iDirection(rhs.m_iDirection)
  1145.         , m_iCurrentDirectionIndex(0)
  1146.         , m_fDistance(rhs.m_fDistance)
  1147.         , m_fDuration(rhs.m_fDuration)
  1148.         , m_bUseLastOp(rhs.m_bUseLastOp)
  1149.         , m_bLookForward(rhs.m_bLookForward)
  1150.         , m_moveStart(ZERO)
  1151.         , m_moveEnd(ZERO)
  1152.         , m_currentDirMask(AI_MOVE_BACKWARD)
  1153.         , m_fMinDistance(rhs.m_fMinDistance)
  1154.         , m_vBestFailedBackoffPos(ZERO)
  1155.         , m_fMaxDistanceFound(0.f)
  1156.         , m_bTryingLessThanMinDistance(false)
  1157.         , m_looseAttentionId(0)
  1158.         , m_bRandomOrder(rhs.m_bRandomOrder)
  1159.         , m_bCheckSlopeDistance(rhs.m_bCheckSlopeDistance)
  1160.         , m_pTraceDirective(nullptr)
  1161.         , m_pPathfindDirective(nullptr)
  1162.         , m_refBackoffPoint()
  1163. {
  1164.         ResetMoveDirections();
  1165. }
  1166.  
  1167. //===================================================================
  1168. // Reset
  1169. //===================================================================
  1170. void COPBackoff::Reset(CPipeUser* pPipeUser)
  1171. {
  1172.         ResetNavigation(pPipeUser);
  1173.         m_fMaxDistanceFound = 0;
  1174.         m_bTryingLessThanMinDistance = false;
  1175.         m_vBestFailedBackoffPos.zero();
  1176.         m_currentDirMask = AI_MOVE_BACKWARD;
  1177.         ResetMoveDirections();
  1178. }
  1179.  
  1180. //===================================================================
  1181. // SetMoveDirections
  1182. //===================================================================
  1183. void COPBackoff::ResetMoveDirections()
  1184. {
  1185.         m_iCurrentDirectionIndex = 0;
  1186.         m_MoveDirections.clear();
  1187.  
  1188.         int mask = (AI_MOVE_FORWARD | AI_MOVE_BACKWARD | AI_MOVE_LEFT | AI_MOVE_RIGHT | AI_MOVE_TOWARDS_GROUP | AI_MOVE_BACKLEFT | AI_MOVE_BACKRIGHT);
  1189.         mask &= m_iDirection;
  1190.  
  1191.         int currentdir = 1;
  1192.         while (currentdir <= mask)
  1193.         {
  1194.                 if ((mask & currentdir) != 0)
  1195.                         m_MoveDirections.push_back(currentdir);
  1196.                 currentdir <<= 1;
  1197.         }
  1198.         if (m_bRandomOrder)
  1199.         {
  1200.                 int size = m_MoveDirections.size();
  1201.                 for (int i = 0; i < size; ++i)
  1202.                 {
  1203.                         int other = cry_random(0, size - 1);
  1204.                         int temp = m_MoveDirections[i];
  1205.                         m_MoveDirections[i] = m_MoveDirections[other];
  1206.                         m_MoveDirections[other] = temp;
  1207.                 }
  1208.         }
  1209. }
  1210.  
  1211. //===================================================================
  1212. // ResetNavigation
  1213. //===================================================================
  1214. void COPBackoff::ResetNavigation(CPipeUser* pPipeUser)
  1215. {
  1216.         if (m_pPathfindDirective)
  1217.                 delete m_pPathfindDirective;
  1218.         m_pPathfindDirective = 0;
  1219.         if (m_pTraceDirective)
  1220.                 delete m_pTraceDirective;
  1221.         m_pTraceDirective = 0;
  1222.  
  1223.         if (pPipeUser)
  1224.                 pPipeUser->ClearPath("COPBackoff::Reset m_Path");
  1225.  
  1226.         if (m_refBackoffPoint.IsNil())
  1227.         {
  1228.                 m_refBackoffPoint.Release();
  1229.                 if (m_bLookForward && pPipeUser)
  1230.                 {
  1231.                         pPipeUser->SetLooseAttentionTarget(NILREF, m_looseAttentionId);
  1232.                 }
  1233.         }
  1234.         m_looseAttentionId = 0;
  1235. }
  1236.  
  1237. //===================================================================
  1238. // Execute
  1239. //===================================================================
  1240. void COPBackoff::ExecuteDry(CPipeUser* pPipeUser)
  1241. {
  1242.         FUNCTION_PROFILER(GetISystem(), PROFILE_AI);
  1243.         if (m_pTraceDirective)
  1244.                 m_pTraceDirective->ExecuteTrace(pPipeUser, false);
  1245. }
  1246.  
  1247. //===================================================================
  1248. // Execute
  1249. //===================================================================
  1250. EGoalOpResult COPBackoff::Execute(CPipeUser* pPipeUser)
  1251. {
  1252.         CCCPOINT(COPBackoff_Execute);
  1253.         FUNCTION_PROFILER(GetISystem(), PROFILE_AI);
  1254.  
  1255.         m_fLastUpdateTime = GetAISystem()->GetFrameStartTime();
  1256.  
  1257.         if (!m_pPathfindDirective)
  1258.         {
  1259.                 CAIObject* pTarget = NULL;
  1260.  
  1261.                 if (m_bUseLastOp)
  1262.                 {
  1263.                         pTarget = pPipeUser->m_refLastOpResult.GetAIObject();
  1264.                         if (!pTarget)
  1265.                         {
  1266.                                 Reset(pPipeUser);
  1267.                                 return eGOR_FAILED;
  1268.                         }
  1269.                 }
  1270.                 else
  1271.                 {
  1272.                         pTarget = (CAIObject*)pPipeUser->GetAttentionTarget();
  1273.                         if (!pTarget)
  1274.                         {
  1275.                                 if (!(pTarget = pPipeUser->m_refLastOpResult.GetAIObject()))
  1276.                                 {
  1277.                                         Reset(pPipeUser);
  1278.                                         return eGOR_FAILED;
  1279.                                 }
  1280.                         }
  1281.                 }
  1282.  
  1283.                 int currentDir = m_iCurrentDirectionIndex >= (int) m_MoveDirections.size() ? 0 : m_MoveDirections[m_iCurrentDirectionIndex];
  1284.                 if (m_iCurrentDirectionIndex < (int)m_MoveDirections.size())
  1285.                         m_iCurrentDirectionIndex++;
  1286.  
  1287.                 if (currentDir == 0)
  1288.                 {
  1289.                         if (m_fMaxDistanceFound > 0.f)
  1290.                         {
  1291.                                 // a path less than mindistance required has been found anyway
  1292.                                 CAIObject* pBackoffPoint = m_refBackoffPoint.GetAIObject();
  1293.                                 m_pPathfindDirective = new COPPathFind("Backoff", pBackoffPoint, std::numeric_limits<float>::max(), 0.0f, m_fDistance);
  1294.                                 pPipeUser->m_nPathDecision = PATHFINDER_STILLFINDING;
  1295.                                 m_bTryingLessThanMinDistance = true;
  1296.                                 if (m_pPathfindDirective->Execute(pPipeUser) != eGOR_IN_PROGRESS)
  1297.                                 {
  1298.                                         if (pPipeUser->m_nPathDecision == PATHFINDER_NOPATH)
  1299.                                         {
  1300.                                                 pPipeUser->m_State.vMoveDir.zero();
  1301.                                                 pPipeUser->SetSignal(1, "OnBackOffFailed", 0, 0, gAIEnv.SignalCRCs.m_nOnBackOffFailed);
  1302.                                                 Reset(pPipeUser);
  1303.                                                 return eGOR_FAILED;
  1304.                                         }
  1305.                                 }
  1306.                         }
  1307.                         else
  1308.                         {
  1309.                                 pPipeUser->m_State.vMoveDir.zero();
  1310.                                 pPipeUser->SetSignal(1, "OnBackOffFailed", 0, 0, gAIEnv.SignalCRCs.m_nOnBackOffFailed);
  1311.                                 Reset(pPipeUser);
  1312.                                 return eGOR_FAILED;
  1313.                         }
  1314.                 }
  1315.  
  1316.                 Vec3 mypos = pPipeUser->GetPhysicsPos();
  1317.                 Vec3 tgpos = pTarget->GetPhysicsPos();
  1318.  
  1319.                 // evil hack
  1320.                 CAIActor* pTargetActor = pTarget->CastToCAIActor();
  1321.                 if ((pTargetActor != NULL) && (pTarget->GetType() == AIOBJECT_VEHICLE))
  1322.                 {
  1323.                         tgpos += pTargetActor->m_State.vMoveDir * 10.f;
  1324.                 }
  1325.  
  1326.                 Vec3 vMoveDir = tgpos - mypos;
  1327.                 // zero the z value because for human backoffs it seems that when the z values differ
  1328.                 // by a large amount at close range the direction tracing doesn't work so well
  1329.                 if (!pPipeUser->IsUsing3DNavigation())
  1330.                         vMoveDir.z = 0.0f;
  1331.  
  1332.                 Vec3 avoidPos;
  1333.  
  1334.                 if (m_bUseTargetPosition)
  1335.                         avoidPos = tgpos;
  1336.                 else
  1337.                         avoidPos = mypos;
  1338.  
  1339.                 // This check assumes that the reason of the backoff
  1340.                 // if to move away to specified direction away from a point to avoid.
  1341.                 // If the agent is already far enough, stop immediately.
  1342.                 if (Distance::Point_Point(avoidPos, mypos) > m_fDistance)
  1343.                 {
  1344.                         Reset(pPipeUser);
  1345.                         return eGOR_SUCCEEDED;
  1346.                 }
  1347.  
  1348.                 switch (currentDir)
  1349.                 {
  1350.                 case AI_MOVE_FORWARD:
  1351.                         break;
  1352.                 case AI_MOVE_RIGHT:
  1353.                         vMoveDir.Set(vMoveDir.y, -vMoveDir.x, 0.0f);
  1354.                         break;
  1355.                 case AI_MOVE_LEFT:
  1356.                         vMoveDir.Set(-vMoveDir.y, vMoveDir.x, 0.0f);
  1357.                         break;
  1358.                 case AI_MOVE_BACKLEFT:
  1359.                         vMoveDir = vMoveDir.GetRotated(Vec3Constants<float>::fVec3_OneZ, gf_PI / 4);
  1360.                         break;
  1361.                 case AI_MOVE_BACKRIGHT:
  1362.                         vMoveDir = vMoveDir.GetRotated(Vec3Constants<float>::fVec3_OneZ, -gf_PI / 4);
  1363.                         break;
  1364.                 case AI_MOVE_TOWARDS_GROUP:
  1365.                         {
  1366.                                 // Average direction from the target towards the group.
  1367.                                 vMoveDir.zero();
  1368.                                 int groupId = pPipeUser->GetGroupId();
  1369.                                 AIObjects::iterator it = GetAISystem()->m_mapGroups.find(groupId);
  1370.                                 AIObjects::iterator end = GetAISystem()->m_mapGroups.end();
  1371.                                 for (; it != end && it->first == groupId; ++it)
  1372.                                 {
  1373.                                         CAIObject* pObject = it->second.GetAIObject();
  1374.                                         if (!pObject || !pObject->IsEnabled())
  1375.                                                 continue;
  1376.                                         if (pObject->GetType() != AIOBJECT_ACTOR)
  1377.                                                 continue;
  1378.                                         vMoveDir += pObject->GetPos() - tgpos;
  1379.                                 }
  1380.                                 if (!pPipeUser->IsUsing3DNavigation())
  1381.                                         vMoveDir.z = 0.0f;
  1382.                         }
  1383.                         break;
  1384.                 case AI_MOVE_BACKWARD:
  1385.                 default://
  1386.                         vMoveDir = -vMoveDir;
  1387.                         break;
  1388.                 }
  1389.                 vMoveDir.NormalizeSafe(Vec3Constants<float>::fVec3_OneX);
  1390.                 vMoveDir *= m_fDistance;
  1391.                 Vec3 backoffPos = avoidPos + vMoveDir;
  1392.  
  1393.                 m_moveStart = mypos;
  1394.                 m_moveEnd = backoffPos;
  1395.  
  1396.                 Vec3 offset(0, 0, 0.5f);
  1397.                 GetAISystem()->AddDebugSphere(m_moveStart + offset, 0.1f, 0, 255, 255, 5);
  1398.                 GetAISystem()->AddDebugSphere(backoffPos + offset, 0.2f, 0, 255, 255, 5);
  1399.                 GetAISystem()->AddDebugLine(m_moveStart + offset, backoffPos + offset, 0, 255, 255, 5);
  1400.  
  1401.                 // (MATT) Really crucial part! {2009/02/04}
  1402.                 // create a dummy object to pathfind towards
  1403.                 if (m_refBackoffPoint.IsNil())
  1404.                 {
  1405.                         gAIEnv.pAIObjectManager->CreateDummyObject(m_refBackoffPoint, string(GetNameSafe(pPipeUser)) + "_BackoffPoint");
  1406.                         m_refBackoffPoint.GetAIObject()->SetPos(backoffPos);
  1407.                 }
  1408.  
  1409.                 if (m_bLookForward)
  1410.                         m_looseAttentionId = pPipeUser->SetLooseAttentionTarget(m_refBackoffPoint);
  1411.  
  1412.                 // start pathfinding
  1413.                 CAIObject* pBackoffPoint = m_refBackoffPoint.GetAIObject();
  1414.                 m_pPathfindDirective = new COPPathFind("Backoff", pBackoffPoint, std::numeric_limits<float>::max(), 0.0f, m_fDistance);
  1415.                 pPipeUser->m_nPathDecision = PATHFINDER_STILLFINDING;
  1416.                 if (m_pPathfindDirective->Execute(pPipeUser) != eGOR_IN_PROGRESS)
  1417.                 {
  1418.                         if (pPipeUser->m_nPathDecision == PATHFINDER_NOPATH)
  1419.                         {
  1420.                                 ResetNavigation(pPipeUser);
  1421.                                 return eGOR_IN_PROGRESS;// check next direction
  1422.                         }
  1423.                 }
  1424.                 return eGOR_IN_PROGRESS;
  1425.         } // !m_pPathfindDirective
  1426.  
  1427.         if (pPipeUser->m_nPathDecision == PATHFINDER_PATHFOUND)
  1428.         {
  1429.                 if (m_fMinDistance > 0)
  1430.                 {
  1431.                         float dist = Distance::Point_Point(pPipeUser->GetPos(), pPipeUser->m_OrigPath.GetLastPathPos());
  1432.                         if (dist < m_fMinDistance)
  1433.                         {
  1434.                                 if (m_fMaxDistanceFound < dist)
  1435.                                 {
  1436.                                         m_fMaxDistanceFound = dist;
  1437.                                         CAIObject* pBackoffPoint = m_refBackoffPoint.GetAIObject();
  1438.                                         m_vBestFailedBackoffPos = pBackoffPoint->GetPos();
  1439.                                 }
  1440.                                 ResetNavigation(pPipeUser);
  1441.                                 return eGOR_IN_PROGRESS;// check next direction
  1442.                         }
  1443.                 }
  1444.                 if (!m_pTraceDirective)
  1445.                 {
  1446.                         m_pTraceDirective = new COPTrace(false, defaultTraceEndAccuracy);
  1447.                         pPipeUser->m_Path.GetParams().precalculatedPath = true; // prevent path regeneration
  1448.                         pPipeUser->SetSignal(0, "OnPathFound", NULL, 0, gAIEnv.SignalCRCs.m_nOnPathFound);
  1449.                         m_fInitTime = GetAISystem()->GetFrameStartTime();
  1450.                 }
  1451.  
  1452.                 if (m_fDuration > 0 && (GetAISystem()->GetFrameStartTime() - m_fInitTime).GetSeconds() > m_fDuration)
  1453.                 {
  1454.                         Reset(pPipeUser);
  1455.                         return eGOR_SUCCEEDED;
  1456.                 }
  1457.  
  1458.                 // keep tracing
  1459.                 EGoalOpResult done = m_pTraceDirective->Execute(pPipeUser);
  1460.                 if (done != eGOR_IN_PROGRESS)
  1461.                 {
  1462.                         Reset(pPipeUser);
  1463.                         return done;
  1464.                 }
  1465.                 // If this goal gets reseted during m_pTraceDirective->Execute it means that
  1466.                 // a smart object was used for navigation which inserts a goal pipe which
  1467.                 // does Reset on this goal which sets m_pTraceDirective to NULL! In this case
  1468.                 // we should just report that this goal pipe isn't finished since it will be
  1469.                 // reexecuted after finishing the inserted goal pipe
  1470.                 if (!m_pTraceDirective)
  1471.                         return eGOR_IN_PROGRESS;
  1472.         }
  1473.         else if (pPipeUser->m_nPathDecision == PATHFINDER_NOPATH)
  1474.         {
  1475.                 if (m_bTryingLessThanMinDistance)
  1476.                         pPipeUser->SetSignal(1, "OnBackOffFailed", 0, 0, gAIEnv.SignalCRCs.m_nOnBackOffFailed);
  1477.  
  1478.                 //redo next time for next direction...
  1479.                 if (m_bTryingLessThanMinDistance)
  1480.                 {
  1481.                         ResetNavigation(pPipeUser);
  1482.                         return eGOR_IN_PROGRESS;
  1483.                 }
  1484.                 else
  1485.                 {
  1486.                         //... unless the less-than-min distance path has already been tried
  1487.                         Reset(pPipeUser);
  1488.                         return eGOR_FAILED;
  1489.                 }
  1490.  
  1491.         }
  1492.         else
  1493.         {
  1494.                 m_pPathfindDirective->Execute(pPipeUser);
  1495.         }
  1496.  
  1497.         return eGOR_IN_PROGRESS;
  1498. }
  1499.  
  1500. void COPBackoff::Serialize(TSerialize ser)
  1501. {
  1502.  
  1503.         ser.BeginGroup("COPBackoff");
  1504.         {
  1505.                 ser.Value("m_bUseTargetPosition", m_bUseTargetPosition);
  1506.                 ser.Value("m_iDirection", m_iDirection);
  1507.                 ser.Value("m_iCurrentDirectionIndex", m_iCurrentDirectionIndex);
  1508.                 ser.Value("m_MoveDirections", m_MoveDirections);
  1509.                 ser.Value("m_fDistance", m_fDistance);
  1510.                 ser.Value("m_fDuration", m_fDuration);
  1511.                 ser.Value("m_fInitTime", m_fInitTime);
  1512.                 ser.Value("m_fLastUpdateTime", m_fLastUpdateTime);
  1513.                 ser.Value("m_bUseLastOp", m_bUseLastOp);
  1514.                 ser.Value("m_bLookForward", m_bLookForward);
  1515.                 ser.Value("m_moveStart", m_moveStart);
  1516.                 ser.Value("m_moveEnd", m_moveEnd);
  1517.                 ser.Value("m_currentDirMask", m_currentDirMask);
  1518.                 ser.Value("m_fMinDistance", m_fMinDistance);
  1519.                 ser.Value("m_vBestFailedBackoffPos", m_vBestFailedBackoffPos);
  1520.                 ser.Value("m_fMaxDistanceFound", m_fMaxDistanceFound);
  1521.                 ser.Value("m_bTryingLessThanMinDistance", m_bTryingLessThanMinDistance);
  1522.                 ser.Value("m_looseAttentionId", m_looseAttentionId);
  1523.                 ser.Value("m_bRandomOrder", m_bRandomOrder);
  1524.  
  1525.                 m_refBackoffPoint.Serialize(ser, "m_refBackoffPoint");
  1526.                 if (ser.IsWriting())
  1527.                 {
  1528.                         if (ser.BeginOptionalGroup("TraceDirective", m_pTraceDirective != NULL))
  1529.                         {
  1530.                                 PREFAST_SUPPRESS_WARNING(6011) m_pTraceDirective->Serialize(ser);
  1531.                                 ser.EndGroup();
  1532.                         }
  1533.                         if (ser.BeginOptionalGroup("PathFindDirective", m_pPathfindDirective != NULL))
  1534.                         {
  1535.                                 PREFAST_SUPPRESS_WARNING(6011) m_pPathfindDirective->Serialize(ser);
  1536.                                 ser.EndGroup();
  1537.                         }
  1538.                 }
  1539.                 else
  1540.                 {
  1541.                         SAFE_DELETE(m_pTraceDirective);
  1542.                         if (ser.BeginOptionalGroup("TraceDirective", true))
  1543.                         {
  1544.                                 m_pTraceDirective = new COPTrace(true);
  1545.                                 m_pTraceDirective->Serialize(ser);
  1546.                                 ser.EndGroup();
  1547.                         }
  1548.                         SAFE_DELETE(m_pPathfindDirective);
  1549.                         if (ser.BeginOptionalGroup("PathFindDirective", true))
  1550.                         {
  1551.                                 m_pPathfindDirective = new COPPathFind("");
  1552.                                 m_pPathfindDirective->Serialize(ser);
  1553.                                 ser.EndGroup();
  1554.                         }
  1555.                 }
  1556.                 ser.EndGroup();
  1557.  
  1558.         }
  1559. }
  1560.  
  1561. void COPBackoff::DebugDraw(CPipeUser* pPipeUser) const
  1562. {
  1563.         Vec3 dir(m_moveEnd - m_moveStart);
  1564.         dir.NormalizeSafe();
  1565.         CDebugDrawContext dc;
  1566.         ColorB color(255, 255, 255);
  1567.         dc->DrawLine(m_moveStart + Vec3(0, 0, 0.25f), color, m_moveEnd + Vec3(0, 0, 0.25f), color);
  1568.         dc->DrawCone(m_moveEnd + Vec3(0, 0, 0.25f), dir, 0.3f, 0.8f, color);
  1569. }
  1570.  
  1571. COPTimeout::COPTimeout(float fIntervalMin, float fIntervalMax) :
  1572.         m_fIntervalMin(fIntervalMin),
  1573.         m_fIntervalMax((fIntervalMax > 0.f) ? fIntervalMax : fIntervalMin)
  1574. {
  1575.         Reset(0);
  1576. }
  1577.  
  1578. COPTimeout::COPTimeout(const XmlNodeRef& node) :
  1579.         m_fIntervalMin(0.f),
  1580.         m_fIntervalMax(-1.f)
  1581. {
  1582.         if (!node->getAttr("interval", m_fIntervalMin))
  1583.         {
  1584.                 s_xml.GetMandatory(node, "intervalMin", m_fIntervalMin);
  1585.         }
  1586.  
  1587.         node->getAttr("intervalMax", m_fIntervalMax);
  1588.         if (m_fIntervalMax <= 0.f)
  1589.         {
  1590.                 m_fIntervalMax = m_fIntervalMin;
  1591.         }
  1592.  
  1593.         Reset(0);
  1594. }
  1595.  
  1596. void COPTimeout::Reset(CPipeUser* pPipeUser)
  1597. {
  1598.         m_actualIntervalMs = 0;
  1599.         m_startTime.SetValue(0);
  1600. }
  1601.  
  1602. void COPTimeout::Serialize(TSerialize ser)
  1603. {
  1604.         uint64 timeElapsed = 0;
  1605.         if (ser.IsWriting())
  1606.         {
  1607.                 CTimeValue time = GetAISystem()->GetFrameStartTime();
  1608.                 timeElapsed = (time - m_startTime).GetMilliSecondsAsInt64();
  1609.         }
  1610.         ser.Value("timeElapsed", timeElapsed);
  1611.         if (ser.IsReading())
  1612.         {
  1613.                 CTimeValue time = GetAISystem()->GetFrameStartTime();
  1614.                 m_startTime.SetMilliSeconds(time.GetMilliSecondsAsInt64() - timeElapsed);
  1615.         }
  1616.         ser.Value("m_actualIntervalMs", m_actualIntervalMs);
  1617. }
  1618.  
  1619. EGoalOpResult COPTimeout::Execute(CPipeUser* pPipeUser)
  1620. {
  1621.         FUNCTION_PROFILER(GetISystem(), PROFILE_AI);
  1622.         CTimeValue time = GetAISystem()->GetFrameStartTime();
  1623.         if (m_startTime.GetMilliSecondsAsInt64() <= 0)
  1624.         {
  1625.                 m_actualIntervalMs = (int)(1000.0f * (m_fIntervalMin + ((m_fIntervalMax - m_fIntervalMin) * cry_random(0.0f, 1.0f))));
  1626.                 m_startTime = time;
  1627.         }
  1628.  
  1629.         CTimeValue timeElapsed = time - m_startTime;
  1630.         if (timeElapsed.GetMilliSecondsAsInt64() > m_actualIntervalMs)
  1631.         {
  1632.                 Reset(pPipeUser);
  1633.                 return eGOR_DONE;
  1634.         }
  1635.  
  1636.         return eGOR_IN_PROGRESS;
  1637. }
  1638.  
  1639. COPStrafe::COPStrafe(float fDistanceStart, float fDistanceEnd, bool bStrafeWhileMoving) :
  1640.         m_fDistanceStart(fDistanceStart),
  1641.         m_fDistanceEnd(fDistanceEnd),
  1642.         m_bStrafeWhileMoving(bStrafeWhileMoving)
  1643. {
  1644. }
  1645.  
  1646. COPStrafe::COPStrafe(const XmlNodeRef& node) :
  1647.         m_fDistanceStart(0.f),
  1648.         m_fDistanceEnd(0.0f),
  1649.         m_bStrafeWhileMoving(s_xml.GetBool(node, "strafeWhileMoving"))
  1650. {
  1651.         if (!node->getAttr("distance", m_fDistanceStart))
  1652.         {
  1653.                 node->getAttr("distanceStart", m_fDistanceStart);
  1654.         }
  1655.  
  1656.         if (!node->getAttr("distanceEnd", m_fDistanceEnd))
  1657.         {
  1658.                 m_fDistanceEnd = m_fDistanceStart;
  1659.         }
  1660. }
  1661.  
  1662. EGoalOpResult COPStrafe::Execute(CPipeUser* pPipeUser)
  1663. {
  1664.         FUNCTION_PROFILER(GetISystem(), PROFILE_AI);
  1665.         if (CPuppet* pPuppet = pPipeUser->CastToCPuppet())
  1666.         {
  1667.                 pPuppet->SetAllowedStrafeDistances(m_fDistanceStart, m_fDistanceEnd, m_bStrafeWhileMoving);
  1668.         }
  1669.         else
  1670.         {
  1671.                 assert(!!!"Goalop 'Strafe' is applicable only to puppets.");
  1672.         }
  1673.         return eGOR_DONE;
  1674. }
  1675.  
  1676. COPFireCmd::COPFireCmd(EFireMode eFireMode, bool bUseLastOp, float fIntervalMin, float fIntervalMax) :
  1677.         m_eFireMode(eFireMode),
  1678.         m_bUseLastOp(bUseLastOp),
  1679.         m_fIntervalMin(fIntervalMin),
  1680.         m_fIntervalMax(fIntervalMax),
  1681.         m_actualIntervalMs(-1000)
  1682. {
  1683.         m_startTime.SetValue(0);
  1684. }
  1685.  
  1686. COPFireCmd::COPFireCmd(const XmlNodeRef& node) :
  1687.         m_eFireMode(FIREMODE_OFF),
  1688.         m_bUseLastOp(s_xml.GetBool(node, "useLastOp")),
  1689.         m_fIntervalMin(-1.f),
  1690.         m_fIntervalMax(-1.f),
  1691.         m_actualIntervalMs(-1000)
  1692. {
  1693.         s_xml.GetFireMode(node, "mode", m_eFireMode, CGoalOpXMLReader::MANDATORY);
  1694.  
  1695.         if (!node->getAttr("timeout", m_fIntervalMin))
  1696.         {
  1697.                 node->getAttr("timeoutMin", m_fIntervalMin);
  1698.         }
  1699.  
  1700.         node->getAttr("timeoutMax", m_fIntervalMax);
  1701.  
  1702.         m_startTime.SetValue(0);
  1703. }
  1704.  
  1705. void COPFireCmd::Reset(CPipeUser* pPipeUser)
  1706. {
  1707.         m_actualIntervalMs = -1000;
  1708.         m_startTime.SetValue(0);
  1709.         pPipeUser->SetFireTarget(NILREF);
  1710. }
  1711.  
  1712. EGoalOpResult COPFireCmd::Execute(CPipeUser* pPipeUser)
  1713. {
  1714.         CCCPOINT(COPFireCmd_Execute);
  1715.         FUNCTION_PROFILER(GetISystem(), PROFILE_AI);
  1716.  
  1717.         CTimeValue time = GetAISystem()->GetFrameStartTime();
  1718.         // if this is a first time
  1719.         if (m_startTime.GetMilliSecondsAsInt64() <= 0)
  1720.         {
  1721.                 pPipeUser->SetFireMode(m_eFireMode);
  1722.                 pPipeUser->SetFireTarget(m_bUseLastOp ? pPipeUser->m_refLastOpResult : NILREF);
  1723.  
  1724.                 m_startTime = time;
  1725.  
  1726.                 if (m_fIntervalMin < 0.0f)
  1727.                         m_actualIntervalMs = -1000;
  1728.                 else if (m_fIntervalMax > 0.0f)
  1729.                         m_actualIntervalMs = (int)(cry_random(m_fIntervalMin, m_fIntervalMax) * 1000.0f);
  1730.                 else
  1731.                         m_actualIntervalMs = (int)(m_fIntervalMin * 1000.0f);
  1732.         }
  1733.  
  1734.         CTimeValue timeElapsed = time - m_startTime;
  1735.  
  1736.         if ((m_actualIntervalMs < 0) || (m_actualIntervalMs < timeElapsed.GetMilliSecondsAsInt64()))
  1737.         {
  1738.                 // stop firing if was timed
  1739.                 if (m_actualIntervalMs > 0)
  1740.                         pPipeUser->SetFireMode(FIREMODE_OFF);
  1741.  
  1742.                 Reset(pPipeUser);
  1743.                 return eGOR_DONE;
  1744.         }
  1745.  
  1746.         return eGOR_IN_PROGRESS;
  1747. }
  1748.  
  1749. COPBodyCmd::COPBodyCmd(EStance nBodyStance, bool bDelayed) :
  1750.         m_nBodyStance(nBodyStance),
  1751.         m_bDelayed(bDelayed)
  1752. {
  1753. }
  1754.  
  1755. COPBodyCmd::COPBodyCmd(const XmlNodeRef& node) :
  1756.         m_nBodyStance(STANCE_NULL),
  1757.         m_bDelayed(s_xml.GetBool(node, "delayed"))
  1758. {
  1759.         s_xml.GetStance(node, "id", m_nBodyStance, CGoalOpXMLReader::MANDATORY);
  1760. }
  1761.  
  1762. EGoalOpResult COPBodyCmd::Execute(CPipeUser* pPipeUser)
  1763. {
  1764.         FUNCTION_PROFILER(GetISystem(), PROFILE_AI);
  1765.         CPuppet* pPuppet = pPipeUser->CastToCPuppet();
  1766.         if (m_bDelayed)
  1767.         {
  1768.                 if (pPuppet)
  1769.                 {
  1770.                         pPuppet->SetDelayedStance(m_nBodyStance);
  1771.                 }
  1772.                 else
  1773.                 {
  1774.                         assert(!!!"COPBodyCmd::Execute: Unable to set a delayed stance on a non-puppet pipe user.");
  1775.                 }
  1776.         }
  1777.         else
  1778.         {
  1779.                 pPipeUser->m_State.bodystate = m_nBodyStance;
  1780.  
  1781.                 if (pPuppet)
  1782.                 {
  1783.                         pPuppet->SetDelayedStance(STANCE_NULL);
  1784.                 }
  1785.         }
  1786.  
  1787.         return eGOR_DONE;
  1788. }
  1789.  
  1790. COPRunCmd::COPRunCmd(float fMaxUrgency, float fMinUrgency, float fScaleDownPathLength) :
  1791.         m_fMaxUrgency(ConvertUrgency(fMaxUrgency)),
  1792.         m_fMinUrgency(ConvertUrgency(fMinUrgency)),
  1793.         m_fScaleDownPathLength(fScaleDownPathLength)
  1794. {
  1795. }
  1796.  
  1797. COPRunCmd::COPRunCmd(const XmlNodeRef& node) :
  1798.         m_fMaxUrgency(0.f),
  1799.         m_fMinUrgency(0.f),
  1800.         m_fScaleDownPathLength(0.f)
  1801. {
  1802.         if (!s_xml.GetUrgency(node, "urgency", m_fMaxUrgency))
  1803.         {
  1804.                 if (!s_xml.GetUrgency(node, "maxUrgency", m_fMaxUrgency))
  1805.                 {
  1806.                         s_xml.GetUrgency(node, "id", m_fMaxUrgency);
  1807.                 }
  1808.         }
  1809.  
  1810.         if ((gAIEnv.configuration.eCompatibilityMode == ECCM_CRYSIS) || (gAIEnv.configuration.eCompatibilityMode == ECCM_CRYSIS2))
  1811.         {
  1812.                 s_xml.GetUrgency(node, "minUrgency", m_fMinUrgency);
  1813.                 node->getAttr("scaleDownPathLength", m_fScaleDownPathLength);
  1814.         }
  1815.  
  1816.         m_fMaxUrgency = ConvertUrgency(m_fMaxUrgency);
  1817.         m_fMinUrgency = ConvertUrgency(m_fMinUrgency);
  1818. }
  1819.  
  1820. float COPRunCmd::ConvertUrgency(float speed)
  1821. {
  1822.         // does NOT match CFlowNode_AIBase<TDerived, TBlocking>::SetSpeed
  1823.         if (speed < -4.5f)
  1824.                 return -AISPEED_SPRINT;
  1825.         else if (speed < -3.5f)
  1826.                 return -AISPEED_RUN;
  1827.         else if (speed < -2.5f)
  1828.                 return -AISPEED_WALK;
  1829.         else if (speed < -1.5f)
  1830.                 return -AISPEED_SLOW;
  1831.         else if (speed < -0.5f)
  1832.                 return AISPEED_SLOW;
  1833.         else if (speed <= 0.5f)
  1834.                 return AISPEED_WALK;
  1835.         else if (speed + 0.001f < AISPEED_SPRINT)
  1836.                 return AISPEED_RUN;
  1837.         else
  1838.                 return AISPEED_SPRINT;
  1839. }
  1840.  
  1841. EGoalOpResult COPRunCmd::Execute(CPipeUser* pPipeUser)
  1842. {
  1843.         FUNCTION_PROFILER(GetISystem(), PROFILE_AI);
  1844.         pPipeUser->m_State.fMovementUrgency = m_fMaxUrgency;
  1845.  
  1846.         if (CPuppet* pPuppet = pPipeUser->CastToCPuppet())
  1847.         {
  1848.                 if (m_fScaleDownPathLength > 0.00001f)
  1849.                 {
  1850.                         pPuppet->SetAdaptiveMovementUrgency(m_fMinUrgency, m_fMaxUrgency, m_fScaleDownPathLength);
  1851.                 }
  1852.                 else
  1853.                 {
  1854.                         pPuppet->SetAdaptiveMovementUrgency(0.f, 0.f, 0.f);
  1855.                 }
  1856.         }
  1857.  
  1858.         return eGOR_DONE;
  1859. }
  1860.  
  1861. // todo luc fixme
  1862. COPLookAt::COPLookAt(float fStartAngle, float fEndAngle, int mode, bool bBodyTurn, bool bUseLastOp)
  1863.         : m_fStartAngle(fStartAngle)
  1864.         , m_fEndAngle(fEndAngle)
  1865.         , m_fLastDot(0.0f)
  1866.         , m_fAngleThresholdCos(0.98f)
  1867.         , m_eLookStyle(bBodyTurn ? LOOKSTYLE_SOFT : LOOKSTYLE_SOFT_NOLOWER)
  1868.         , m_bUseLastOp(bUseLastOp)
  1869.         , m_bContinuous((mode & AI_LOOKAT_CONTINUOUS) != 0)
  1870.         , m_bInitialized(false)
  1871.         , m_bUseBodyDir((mode & AI_LOOKAT_USE_BODYDIR) != 0)
  1872.         , m_bYawOnly(false)
  1873. {
  1874. }
  1875.  
  1876. COPLookAt::COPLookAt(const XmlNodeRef& node)
  1877.         : m_fStartAngle(0.f)
  1878.         , m_fEndAngle(0.f)
  1879.         , m_fLastDot(0.0f)
  1880.         , m_fAngleThresholdCos(0.98f)
  1881.         , m_eLookStyle(s_xml.GetBool(node, "bodyTurn", true) ? LOOKSTYLE_SOFT : LOOKSTYLE_SOFT_NOLOWER)
  1882.         , m_bUseLastOp(s_xml.GetBool(node, "useLastOp"))
  1883.         , m_bContinuous(s_xml.GetBool(node, "continuous"))
  1884.         , m_bInitialized(false)
  1885.         , m_bUseBodyDir(s_xml.GetBool(node, "useBodyDir"))
  1886.         , m_bYawOnly(s_xml.GetBool(node, "yawOnly"))
  1887. {
  1888.         node->getAttr("startAngle", m_fStartAngle);
  1889.         node->getAttr("endAngle", m_fEndAngle);
  1890.  
  1891.         float fAngleThreshold;
  1892.         if (node->getAttr("angleThreshold", fAngleThreshold))
  1893.                 m_fAngleThresholdCos = cosf(DEG2RAD(fAngleThreshold));
  1894. }
  1895.  
  1896. void COPLookAt::ResetLooseTarget(CPipeUser* pPipeUser, bool bForceReset)
  1897. {
  1898.         if (bForceReset || !m_bContinuous)
  1899.         {
  1900.                 pPipeUser->SetLookStyle(LOOKSTYLE_DEFAULT);
  1901.                 pPipeUser->SetLooseAttentionTarget(NILREF);
  1902.         }
  1903. }
  1904.  
  1905. void COPLookAt::Reset(CPipeUser* pPipeUser)
  1906. {
  1907.         ResetLooseTarget(pPipeUser);
  1908.         m_bInitialized = false;
  1909. }
  1910.  
  1911. EGoalOpResult COPLookAt::Execute(CPipeUser* pPipeUser)
  1912. {
  1913.         CCCPOINT(COPLookAt_Execute)
  1914.         FUNCTION_PROFILER(GetISystem(), PROFILE_AI);
  1915.  
  1916.         if (m_fStartAngle < -450.f) // stop looking at - disable looseAttentionTarget
  1917.         {
  1918.                 ResetLooseTarget(pPipeUser, true);
  1919.                 return eGOR_SUCCEEDED;
  1920.         }
  1921.  
  1922.         // first time
  1923.         if (!m_bInitialized)
  1924.         {
  1925.                 m_bInitialized = true;
  1926.  
  1927.                 m_startTime = GetAISystem()->GetFrameStartTime();
  1928.  
  1929.                 if (!m_bUseLastOp && !m_fStartAngle && !m_fEndAngle)
  1930.                 {
  1931.                         return eGOR_SUCCEEDED;
  1932.                 }
  1933.  
  1934.                 pPipeUser->SetLookStyle(m_eLookStyle);
  1935.  
  1936.                 if (!m_fStartAngle && !m_fEndAngle)
  1937.                 {
  1938.                         CAIObject* pLastOp = pPipeUser->m_refLastOpResult.GetAIObject();
  1939.                         if (!pLastOp)
  1940.                         {
  1941.                                 Reset(pPipeUser);
  1942.                                 return eGOR_FAILED;
  1943.                         }
  1944.  
  1945.                         pPipeUser->SetLooseAttentionTarget(GetWeakRef(pLastOp));
  1946.  
  1947.                         if (m_bContinuous) // will keep the look at target after this goalop ends
  1948.                         {
  1949.                                 Reset(pPipeUser);// Luc 1-aug-07: this reset was missing and it broke non continuous lookat with looping goalpipes (never finishing in the second loop)
  1950.                                 return eGOR_SUCCEEDED;
  1951.                         } // otherwise, wait for orienting like the loose att target
  1952.                 }
  1953.                 else  // if (!m_fStartAngle && !m_fEndAngle)
  1954.                 {
  1955.                         Vec3 vMoveDir = pPipeUser->GetMoveDir();
  1956.                         vMoveDir.z = 0;
  1957.  
  1958.                         float fActualAngle = cry_random(m_fStartAngle, m_fEndAngle);
  1959.                         Matrix33 m = Matrix33::CreateRotationZ(DEG2RAD(fActualAngle));
  1960.                         vMoveDir = m * vMoveDir;
  1961.  
  1962.                         pPipeUser->SetLooseAttentionTarget(pPipeUser->GetPos() + 100.f * vMoveDir);
  1963.                 }
  1964.         }
  1965.         else  // keep on looking, orient like the loose att target
  1966.         {
  1967.                 //m_bInitialized
  1968.  
  1969.                 if (m_bContinuous)
  1970.                         return eGOR_SUCCEEDED;
  1971.  
  1972.                 // Time out if angle threshold isn't reached for some time (to avoid deadlocks)
  1973.                 float elapsedTime = (GetAISystem()->GetFrameStartTime() - m_startTime).GetSeconds();
  1974.                 if (elapsedTime > 8.0f)
  1975.                 {
  1976.                         AIWarning("LookAt goal op timed out. Didn't reach wanted angle threshold for %f seconds.", elapsedTime);
  1977.                         Reset(pPipeUser);
  1978.                         return eGOR_SUCCEEDED;
  1979.                 }
  1980.  
  1981.                 Vec3 vLookAtPos = pPipeUser->GetLooseAttentionPos();
  1982.                 if (vLookAtPos.IsZero(1.f))
  1983.                 {
  1984.                         if (IAIObject* pAttTarget = pPipeUser->GetAttentionTarget())
  1985.                         {
  1986.                                 vLookAtPos = pAttTarget->GetPos();
  1987.                         }
  1988.                 }
  1989.  
  1990.                 Vec3 vToLookTarget = vLookAtPos - pPipeUser->GetPos();
  1991.                 Vec3 vToLookTargetDir = vToLookTarget;
  1992.                 Vec3 vMyDir = m_bUseBodyDir ? pPipeUser->GetBodyDir() : pPipeUser->GetViewDir();
  1993.                 const bool b2D = !pPipeUser->GetMovementAbility().b3DMove;
  1994.                 const bool yawOnly = (m_bYawOnly) || (b2D && m_bUseBodyDir);
  1995.                 if (yawOnly)
  1996.                 {
  1997.                         vMyDir.z = 0;
  1998.                         vMyDir.NormalizeSafe();
  1999.                         vToLookTargetDir.z = 0;
  2000.                 }
  2001.                 vToLookTargetDir.NormalizeSafe();
  2002.  
  2003.                 float f = vMyDir.Dot(vToLookTargetDir);
  2004.                 if (f > m_fAngleThresholdCos)
  2005.                 {
  2006.                         Reset(pPipeUser);
  2007.                         return eGOR_SUCCEEDED;
  2008.                 }
  2009.  
  2010.                 if (b2D)
  2011.                 {
  2012.                         vToLookTarget.z = 0;
  2013.                 }
  2014.  
  2015.                 // To avoid getting stuck
  2016.                 if (vToLookTarget.len() < 0.5f)
  2017.                 {
  2018.                         Reset(pPipeUser);
  2019.                         return eGOR_SUCCEEDED;
  2020.                 }
  2021.         }
  2022.         return eGOR_IN_PROGRESS;
  2023. }
  2024.  
  2025. //
  2026. //----------------------------------------------------------------------------------------------------------
  2027. void COPLookAt::Serialize(TSerialize ser)
  2028. {
  2029.         ser.BeginGroup("COPLookAt");
  2030.         {
  2031.                 ser.Value("m_fStartAngle", m_fStartAngle);
  2032.                 ser.Value("m_fEndAngle", m_fEndAngle);
  2033.                 ser.Value("m_fLastDot", m_fLastDot);
  2034.                 ser.Value("m_bUseLastOp", m_bUseLastOp);
  2035.                 ser.Value("m_bContinuous", m_bContinuous);
  2036.                 ser.Value("m_bUseBodyDir", m_bUseBodyDir);
  2037.                 ser.Value("m_bInitialized", m_bInitialized);
  2038.  
  2039.                 ser.EndGroup();
  2040.         }
  2041. }
  2042.  
  2043. //
  2044. //----------------------------------------------------------------------------------------------------------
  2045. COPLookAround::COPLookAround(float lookAtRange, float scanIntervalRange, float intervalMin, float intervalMax, bool bBodyTurn, bool breakOnLiveTarget, bool bUseLastOp, bool checkForObstacles)
  2046.         : m_fLastDot(0.0f)
  2047.         , m_fLookAroundRange(lookAtRange)
  2048.         , m_fIntervalMin(intervalMin)
  2049.         , m_fIntervalMax(intervalMax)
  2050.         , m_fScanIntervalRange(scanIntervalRange)
  2051.         , m_scanTimeOutMs(0)
  2052.         , m_timeOutMs(0)
  2053.         , m_eLookStyle(LOOKSTYLE_DEFAULT)
  2054.         , m_breakOnLiveTarget(breakOnLiveTarget)
  2055.         , m_useLastOp(bUseLastOp)
  2056.         , m_lookAngle(0.0f)
  2057.         , m_lookZOffset(0.0f)
  2058.         , m_lastLookAngle(0.0f)
  2059.         , m_lastLookZOffset(0.0f)
  2060.         , m_looseAttentionId(0)
  2061.         , m_bInitialized(false)
  2062.         , m_initialDir(1, 0, 0)
  2063.         , m_checkForObstacles(checkForObstacles)
  2064. {
  2065.         if (m_fIntervalMax < m_fIntervalMin)
  2066.                 m_fIntervalMax = m_fIntervalMin;
  2067.  
  2068.         RandomizeScanTimeout();
  2069.         RandomizeTimeout();
  2070.  
  2071.         m_eLookStyle = (bBodyTurn ? LOOKSTYLE_SOFT : LOOKSTYLE_SOFT_NOLOWER);
  2072. }
  2073.  
  2074. COPLookAround::COPLookAround(const XmlNodeRef& node)
  2075.         : m_fLastDot(0.0f)
  2076.         , m_fLookAroundRange(0.f)
  2077.         , m_fIntervalMin(-1.f)
  2078.         , m_fIntervalMax(-1.f)
  2079.         , m_fScanIntervalRange(-1.f)
  2080.         , m_scanTimeOutMs(0)
  2081.         , m_timeOutMs(0)
  2082.         , m_eLookStyle(s_xml.GetBool(node, "bodyTurn", true) ? LOOKSTYLE_SOFT : LOOKSTYLE_SOFT_NOLOWER)
  2083.         , m_breakOnLiveTarget(s_xml.GetBool(node, "breakOnLiveTarget"))
  2084.         , m_useLastOp(s_xml.GetBool(node, "useLastOp"))
  2085.         , m_lookAngle(0.f)
  2086.         , m_lookZOffset(0.f)
  2087.         , m_lastLookAngle(0.f)
  2088.         , m_lastLookZOffset(0.f)
  2089.         , m_looseAttentionId(0)
  2090.         , m_bInitialized(false)
  2091.         , m_initialDir(1.f, 0.f, 0.f)
  2092.         , m_checkForObstacles(false)
  2093. {
  2094.         s_xml.GetMandatory(node, "lookAroundRange", m_fLookAroundRange);
  2095.         node->getAttr("scanRange", m_fScanIntervalRange);
  2096.  
  2097.         if (node->getAttr("interval", m_fIntervalMin))
  2098.         {
  2099.                 m_fIntervalMax = m_fIntervalMin;
  2100.         }
  2101.         node->getAttr("intervalMin", m_fIntervalMin);
  2102.         node->getAttr("intervalMax", m_fIntervalMax);
  2103.         if (m_fIntervalMax < m_fIntervalMin)
  2104.         {
  2105.                 m_fIntervalMax = m_fIntervalMin;
  2106.         }
  2107.  
  2108.         RandomizeScanTimeout();
  2109.         RandomizeTimeout();
  2110.  
  2111.         node->getAttr("checkForObstacles", m_checkForObstacles);
  2112. }
  2113.  
  2114. void COPLookAround::UpdateLookAtTarget(CPipeUser* pPipeUser)
  2115. {
  2116.         m_lastLookAngle = m_lookAngle;
  2117.         m_lastLookZOffset = m_lookZOffset;
  2118.  
  2119.         if (!m_checkForObstacles)
  2120.         {
  2121.                 SetRandomLookDir();
  2122.         }
  2123.         else
  2124.         {
  2125.                 const Vec3& opPos = pPipeUser->GetPos();
  2126.                 Vec3 dir;
  2127.  
  2128.                 uint32 limitRays = 4;
  2129.                 for (uint32 i = 0; i < limitRays; ++i)
  2130.                 {
  2131.                         SetRandomLookDir();
  2132.                         dir = GetLookAtDir(pPipeUser, m_lookAngle, m_lookZOffset);
  2133.                         // TODO : Defer this ray cast
  2134.                         if (GetAISystem()->CheckPointsVisibility(opPos, opPos + dir, 5.0f))
  2135.                                 break;
  2136.                 }
  2137.         }
  2138. }
  2139.  
  2140. void COPLookAround::SetRandomLookDir()
  2141. {
  2142.         // Random angle within range.
  2143.         float range(gf_PI);
  2144.         if (m_fLookAroundRange > 0)
  2145.                 range = DEG2RAD(m_fLookAroundRange);
  2146.         range = clamp_tpl(range, 0.0f, gf_PI);
  2147.         m_lookAngle = cry_random(-1.0f, 1.0f) * range;
  2148.         m_lookZOffset = cry_random(-3.0f, 0.3f);  // Look more down then up
  2149. }
  2150.  
  2151. Vec3 COPLookAround::GetLookAtDir(CPipeUser* pPipeUser, float angle, float dz) const
  2152. {
  2153.         Vec3 forward = m_initialDir;
  2154.         if (!pPipeUser->GetState().vMoveDir.IsZero())
  2155.                 forward = pPipeUser->GetState().vMoveDir;
  2156.         if (m_useLastOp)
  2157.         {
  2158.                 CAIObject* pLastOpResult = pPipeUser->m_refLastOpResult.GetAIObject();
  2159.                 if (pLastOpResult)
  2160.                         forward = (pLastOpResult->GetPos() - pPipeUser->GetPos()).GetNormalizedSafe();
  2161.         }
  2162.         forward.z = 0.0f;
  2163.         forward.NormalizeSafe();
  2164.         Vec3 right(forward.y, -forward.x, 0.0f);
  2165.  
  2166.         const float lookAtDist = 20.0f;
  2167.         float dx = (float)sin_tpl(angle) * lookAtDist;
  2168.         float dy = (float)cos_tpl(angle) * lookAtDist;
  2169.  
  2170.         return right * dx + forward * dy + Vec3(0, 0, dz);
  2171. }
  2172.  
  2173. EGoalOpResult COPLookAround::Execute(CPipeUser* pPipeUser)
  2174. {
  2175.         FUNCTION_PROFILER(GetISystem(), PROFILE_AI);
  2176.  
  2177.         if (!m_bInitialized)
  2178.         {
  2179.                 m_initialDir = pPipeUser->GetEntityDir();
  2180.                 m_bInitialized = true;
  2181.         }
  2182.  
  2183.         if (!m_looseAttentionId)
  2184.         {
  2185.                 m_fLastDot = 10.0f;
  2186.  
  2187.                 UpdateLookAtTarget(pPipeUser);
  2188.                 Vec3 dir = GetLookAtDir(pPipeUser, m_lookAngle, m_lookZOffset);
  2189.                 Vec3 pos(pPipeUser->GetPos() + dir);
  2190.                 m_looseAttentionId = pPipeUser->SetLooseAttentionTarget(pos);
  2191.                 m_startTime = m_scanStartTime = GetAISystem()->GetFrameStartTime();
  2192.  
  2193.                 pPipeUser->SetLookStyle(m_eLookStyle);
  2194.         }
  2195.         else
  2196.         {
  2197.                 int id = pPipeUser->GetLooseAttentionId();
  2198.                 if (id && id != m_looseAttentionId)
  2199.                 {
  2200.                         //something else is using the operand loose target; aborting
  2201.                         Reset(pPipeUser);
  2202.                         return eGOR_FAILED;
  2203.                 }
  2204.  
  2205.                 if (m_breakOnLiveTarget && pPipeUser->GetAttentionTarget() && pPipeUser->GetAttentionTarget()->IsAgent())
  2206.                 {
  2207.                         pPipeUser->SetLooseAttentionTarget(NILREF, m_looseAttentionId);
  2208.                         m_looseAttentionId = 0;
  2209.                         Reset(pPipeUser);
  2210.                         return eGOR_SUCCEEDED;
  2211.                 }
  2212.  
  2213.                 // (MATT) Test for possible div by 0. Unpretty but should work. {2009/04/28}
  2214.                 if (pPipeUser->GetAttentionTarget() && (pPipeUser->GetAttentionTarget()->GetPos().GetDistance(pPipeUser->GetPos()) < 0.0001f))
  2215.                         return eGOR_FAILED;
  2216.  
  2217.                 ExecuteDry(pPipeUser);
  2218.  
  2219.                 if (m_timeOutMs < 0)
  2220.                 {
  2221.                         // If no time out is specified, we bail out once the target is reached.
  2222.                         const Vec3& opPos = pPipeUser->GetPos();
  2223.                         const Vec3& opViewDir = pPipeUser->GetViewDir();
  2224.  
  2225.                         Vec3 dirToTarget = pPipeUser->GetLooseAttentionPos() - opPos;
  2226.                         dirToTarget.NormalizeSafe();
  2227.  
  2228.                         float f = opViewDir.Dot(dirToTarget);
  2229.                         if (f > 0.99f || fabsf(f - m_fLastDot) < 0.001f)
  2230.                         {
  2231.                                 Reset(pPipeUser);
  2232.  
  2233.                                 return eGOR_SUCCEEDED;
  2234.                         }
  2235.                         else
  2236.                                 m_fLastDot = f;
  2237.                 }
  2238.                 else
  2239.                 {
  2240.                         // If time out is specified, keep looking around until the time out finishes.
  2241.                         int64 elapsed = (GetAISystem()->GetFrameStartTime() - m_startTime).GetMilliSecondsAsInt64();
  2242.  
  2243.                         if (elapsed > m_timeOutMs)
  2244.                         {
  2245.                                 Reset(pPipeUser);
  2246.  
  2247.                                 return eGOR_SUCCEEDED;
  2248.                         }
  2249.                 }
  2250.         }
  2251.  
  2252.         return eGOR_IN_PROGRESS;
  2253. }
  2254.  
  2255. void COPLookAround::ExecuteDry(CPipeUser* pPipeUser)
  2256. {
  2257.         FUNCTION_PROFILER(GetISystem(), PROFILE_AI);
  2258.         if (!m_bInitialized)
  2259.                 return;
  2260.  
  2261.         CTimeValue now(GetAISystem()->GetFrameStartTime());
  2262.         int64 scanElapsedMs = (now - m_scanStartTime).GetMilliSecondsAsInt64();
  2263.  
  2264.         if (pPipeUser->GetAttentionTarget())
  2265.         {
  2266.                 // Interpolate the initial dir towards the current attention target slowly.
  2267.                 Vec3 reqDir = pPipeUser->GetAttentionTarget()->GetPos() - pPipeUser->GetPos();
  2268.                 reqDir.Normalize();
  2269.  
  2270.                 const float maxRatePerSec = DEG2RAD(25.0f);
  2271.                 const float maxRate = maxRatePerSec * GetAISystem()->GetFrameDeltaTime();
  2272.                 const float thr = cosf(maxRate);
  2273.  
  2274.                 float cosAngle = m_initialDir.Dot(reqDir);
  2275.                 if (cosAngle > thr)
  2276.                 {
  2277.                         m_initialDir = reqDir;
  2278.                 }
  2279.                 else
  2280.                 {
  2281.                         float angle = acos_tpl(cosAngle);
  2282.                         float t = 0.0f;
  2283.                         if (angle != 0.0f)
  2284.                                 t = maxRate / angle;
  2285.                         Quat curView;
  2286.                         curView.SetRotationVDir(m_initialDir);
  2287.                         Quat reqView;
  2288.                         reqView.SetRotationVDir(reqDir);
  2289.                         Quat view;
  2290.                         view.SetSlerp(curView, reqView, t);
  2291.                         m_initialDir = view * FORWARD_DIRECTION;
  2292.                 }
  2293.         }
  2294.  
  2295.         // Smooth transition from last value to new value if scanning, otherwise just as fast as possible.
  2296.         float t = 1.0f;
  2297.         if (m_scanTimeOutMs > 0)
  2298.                 t = (1.0f - cosf(clamp_tpl(static_cast<float>(scanElapsedMs) / static_cast<float>(m_scanTimeOutMs), 0.0f, 1.0f) * gf_PI)) * 0.5f;
  2299.  
  2300.         Vec3 dir = GetLookAtDir(pPipeUser, m_lastLookAngle + (m_lookAngle - m_lastLookAngle) * t, m_lastLookZOffset + (m_lookZOffset - m_lastLookZOffset) * t);
  2301.         Vec3 pos = pPipeUser->GetPos() + dir;
  2302.  
  2303.         m_looseAttentionId = pPipeUser->SetLooseAttentionTarget(pos);
  2304.  
  2305.         // Once one sweep is finished, start another.
  2306.         if (scanElapsedMs > m_scanTimeOutMs)
  2307.         {
  2308.                 UpdateLookAtTarget(pPipeUser);
  2309.  
  2310.                 m_scanStartTime = now;
  2311.                 RandomizeScanTimeout();
  2312.         }
  2313. }
  2314.  
  2315. void COPLookAround::Reset(CPipeUser* pPipeUser)
  2316. {
  2317.         m_bInitialized = false;
  2318.         if (pPipeUser)
  2319.         {
  2320.                 pPipeUser->SetLooseAttentionTarget(NILREF, m_looseAttentionId);
  2321.                 m_looseAttentionId = 0;
  2322.         }
  2323.  
  2324.         RandomizeScanTimeout();
  2325.         RandomizeTimeout();
  2326.  
  2327.         m_lookAngle = 0.0f;
  2328.         m_lookZOffset = 0.0f;
  2329.         m_lastLookAngle = 0.0f;
  2330.         m_lastLookZOffset = 0.0f;
  2331.  
  2332.         m_fLastDot = 0;
  2333.         //      m_fTimeOut = 0;
  2334.         //m_fScanTimeOut = 0;
  2335.         if (pPipeUser)
  2336.                 pPipeUser->SetLookStyle(LOOKSTYLE_DEFAULT);
  2337. }
  2338.  
  2339. //===================================================================
  2340. // DebugDraw
  2341. //===================================================================
  2342. void COPLookAround::DebugDraw(CPipeUser* pPipeUser) const
  2343. {
  2344.         int64 scanElapsed = (GetAISystem()->GetFrameStartTime() - m_scanStartTime).GetMilliSecondsAsInt64();
  2345.  
  2346.         float t(1.0f);
  2347.         if (m_scanTimeOutMs > 0)
  2348.                 t = (1.0f - cosf(clamp_tpl(scanElapsed / (m_scanTimeOutMs * 0.001f), 0.0f, 1.0f) * gf_PI)) / 2.0f;
  2349.         Vec3 dir = GetLookAtDir(pPipeUser, m_lastLookAngle + (m_lookAngle - m_lastLookAngle) * t, m_lastLookZOffset + (m_lookZOffset - m_lastLookZOffset) * t);
  2350.  
  2351.         CDebugDrawContext dc;
  2352.  
  2353.         Vec3 pos = pPipeUser->GetPos() - Vec3(0, 0, 1);
  2354.  
  2355.         // Initial dir
  2356.         dc->DrawLine(pos, ColorB(255, 255, 255), pos + m_initialDir * 3.0f, ColorB(255, 255, 255, 128));
  2357.  
  2358.         // Current dir
  2359.         dc->DrawLine(pos, ColorB(255, 0, 0), pos + dir * 3.0f, ColorB(255, 0, 0, 128), 3.0f);
  2360. }
  2361.  
  2362. void COPLookAround::Serialize(TSerialize ser)
  2363. {
  2364.  
  2365.         ser.BeginGroup("COPLookAround");
  2366.         {
  2367.                 ser.Value("m_fLastDot", m_fLastDot);
  2368.                 ser.Value("m_fLookAroundRange", m_fLookAroundRange);
  2369.                 ser.Value("m_fIntervalMin", m_fIntervalMin);
  2370.                 ser.Value("m_fIntervalMax", m_fIntervalMax);
  2371.                 ser.Value("m_fTimeOutMs", m_timeOutMs);
  2372.                 ser.Value("m_fScanIntervalRange", m_fScanIntervalRange);
  2373.                 ser.Value("m_scanTimeOutMs", m_scanTimeOutMs);
  2374.                 ser.Value("m_startTime", m_startTime);
  2375.                 ser.Value("m_scanStartTime", m_scanStartTime);
  2376.                 ser.Value("m_breakOnLiveTarget", m_breakOnLiveTarget);
  2377.                 ser.Value("m_useLastOp", m_useLastOp);
  2378.                 ser.Value("m_lookAngle", m_lookAngle);
  2379.                 ser.Value("m_lookZOffset", m_lookZOffset);
  2380.                 ser.Value("m_lastLookAngle", m_lastLookAngle);
  2381.                 ser.Value("m_lastLookZOffset", m_lastLookZOffset);
  2382.                 ser.Value("m_looseAttentionId", m_looseAttentionId);
  2383.  
  2384.                 ser.EndGroup();
  2385.         }
  2386.  
  2387. }
  2388. //
  2389. //-------------------------------------------------------------------------------------------------------------------------------
  2390. COPPathFind::COPPathFind(
  2391.   const char* szTargetName,
  2392.   CAIObject* pTarget,
  2393.   float fEndTolerance,
  2394.   float fEndDistance,
  2395.   float fDirectionOnlyDistance) :
  2396.  
  2397.         m_sTargetName(szTargetName),
  2398.         m_refTarget(GetWeakRef(pTarget)),
  2399.         m_fDirectionOnlyDistance(fDirectionOnlyDistance),
  2400.         m_fEndTolerance(fEndTolerance),
  2401.         m_fEndDistance(fEndDistance),
  2402.         m_vTargetPos(ZERO),
  2403.         m_vTargetOffset(ZERO),
  2404.         m_nForceTargetBuildingID(-1),
  2405.         m_bWaitingForResult(false)
  2406. {
  2407. }
  2408.  
  2409. //
  2410. //-------------------------------------------------------------------------------------------------------------------------------
  2411. COPPathFind::COPPathFind(const XmlNodeRef& node) :
  2412.         m_sTargetName(s_xml.GetMandatoryString(node, "target")),
  2413.         m_refTarget(m_sTargetName.empty() ? NILREF : GetWeakRef(gAIEnv.pAIObjectManager->GetAIObjectByName(m_sTargetName.c_str()))),
  2414.         m_fDirectionOnlyDistance(0.f),
  2415.         m_fEndTolerance(0.f),
  2416.         m_fEndDistance(0.f),
  2417.         m_vTargetPos(ZERO),
  2418.         m_vTargetOffset(ZERO),
  2419.         m_nForceTargetBuildingID(-1),
  2420.         m_bWaitingForResult(false)
  2421. {
  2422.         node->getAttr("directionOnlyDistance", m_fDirectionOnlyDistance);
  2423. }
  2424.  
  2425. //
  2426. //-------------------------------------------------------------------------------------------------------------------------------
  2427. void COPPathFind::Reset(CPipeUser* pPipeUser)
  2428. {
  2429.         CCCPOINT(COPPathFind_Reset);
  2430.  
  2431.         m_bWaitingForResult = false;
  2432.         m_nForceTargetBuildingID = -1;
  2433.         // (MATT) Note that apparently we don't reset the target here {2009/02/17}
  2434. }
  2435.  
  2436. //
  2437. //-------------------------------------------------------------------------------------------------------------------------------
  2438. EGoalOpResult COPPathFind::Execute(CPipeUser* pPipeUser)
  2439. {
  2440.         CCCPOINT(COPPathFind_Execute);
  2441.         FUNCTION_PROFILER(GetISystem(), PROFILE_AI);
  2442.  
  2443.         if (!m_bWaitingForResult)
  2444.         {
  2445.                 CAIObject* pTarget = 0;
  2446.  
  2447.                 if (!m_sTargetName.empty())
  2448.                 {
  2449.                         pTarget = pPipeUser->GetSpecialAIObject(m_sTargetName.c_str());
  2450.                 }
  2451.  
  2452.                 if (!pTarget)
  2453.                 {
  2454.                         pTarget = m_refTarget.GetAIObject();
  2455.  
  2456.                         if (!pTarget)
  2457.                         {
  2458.                                 pTarget = pPipeUser->m_refLastOpResult.GetAIObject();
  2459.  
  2460.                                 if (!pTarget)
  2461.                                 {
  2462.                                         pPipeUser->SetSignal(0, "OnNoPathFound", 0, 0, gAIEnv.SignalCRCs.m_nOnNoPathFound);
  2463.                                         pPipeUser->m_nPathDecision = PATHFINDER_NOPATH;
  2464.                                         Reset(pPipeUser);
  2465.                                         return eGOR_FAILED;
  2466.                                 }
  2467.                         }
  2468.                 }
  2469.  
  2470.                 CCCPOINT(COPPathFind_Execute_RequestPath);
  2471.  
  2472.                 m_vTargetPos = pTarget->GetPhysicsPos() + m_vTargetOffset;
  2473.  
  2474.                 if (m_fDirectionOnlyDistance > 0.f)
  2475.                 {
  2476.                         pPipeUser->RequestPathInDirection(m_vTargetPos, m_fDirectionOnlyDistance, NILREF, m_fEndDistance);
  2477.                 }
  2478.                 else
  2479.                 {
  2480.                         pPipeUser->RequestPathTo(m_vTargetPos, pTarget->GetMoveDir(),
  2481.                                                  pTarget->GetSubType() != CAIObject::STP_FORMATION,
  2482.                                                  m_nForceTargetBuildingID, m_fEndTolerance, m_fEndDistance, pTarget);
  2483.                 }
  2484.  
  2485.                 m_bWaitingForResult = true;
  2486.         }
  2487.  
  2488.         switch (pPipeUser->m_nPathDecision)
  2489.         {
  2490.         case PATHFINDER_STILLFINDING:
  2491.                 return eGOR_IN_PROGRESS;
  2492.  
  2493.         case PATHFINDER_NOPATH:
  2494.                 pPipeUser->SetSignal(0, "OnNoPathFound", 0, 0, gAIEnv.SignalCRCs.m_nOnNoPathFound);
  2495.                 Reset(pPipeUser);
  2496.                 return eGOR_FAILED;
  2497.  
  2498.         default:
  2499.                 if (pPipeUser->m_Path.GetPath().empty())
  2500.                 {
  2501.                         pPipeUser->SetSignal(0, "OnNoPathFound", 0, 0, gAIEnv.SignalCRCs.m_nOnNoPathFound);
  2502.                 }
  2503.  
  2504.                 Reset(pPipeUser);
  2505.                 return eGOR_SUCCEEDED;
  2506.         }
  2507. }
  2508.  
  2509. void COPPathFind::Serialize(TSerialize ser)
  2510. {
  2511.         ser.BeginGroup("COPPathFind");
  2512.         {
  2513.                 m_refTarget.Serialize(ser, "m_refTarget");
  2514.                 ser.Value("m_sTargetName", m_sTargetName);
  2515.                 ser.Value("m_fDirectionOnlyDistance", m_fDirectionOnlyDistance);
  2516.                 ser.Value("m_fEndTolerance", m_fEndTolerance);
  2517.                 ser.Value("m_fEndDistance", m_fEndDistance);
  2518.                 ser.Value("m_vTargetPos", m_vTargetPos);
  2519.                 ser.Value("m_vTargetOffset", m_vTargetOffset);
  2520.                 ser.Value("m_nForceTargetBuildingID", m_nForceTargetBuildingID);
  2521.  
  2522.                 ser.Value("m_bWaitingForResult", m_bWaitingForResult);
  2523.  
  2524.                 ser.EndGroup();
  2525.         }
  2526. }
  2527.  
  2528. COPLocate::COPLocate(const char* szName, unsigned short int nObjectType, float fRange) :
  2529.         m_sName(szName), m_nObjectType(nObjectType), m_fRange(fRange), m_bWarnIfFailed(true)
  2530. {
  2531. }
  2532.  
  2533. COPLocate::COPLocate(const XmlNodeRef& node) :
  2534.         m_sName(node->getAttr("name")),
  2535.         m_nObjectType(AIOBJECT_DUMMY),
  2536.         m_fRange(0.f),
  2537.         m_bWarnIfFailed(s_xml.GetBool(node, "warnIfFailed", true))
  2538. {
  2539.         if (m_sName.empty())
  2540.         {
  2541.                 s_xml.GetAIObjectType(node, "type", m_nObjectType, CGoalOpXMLReader::MANDATORY);
  2542.         }
  2543.  
  2544.         node->getAttr("range", m_fRange);
  2545. }
  2546.  
  2547. EGoalOpResult COPLocate::Execute(CPipeUser* pPipeUser)
  2548. {
  2549.         CCCPOINT(COPLocate_Execute);
  2550.         FUNCTION_PROFILER(GetISystem(), PROFILE_AI);
  2551.  
  2552.         if (m_sName.empty())
  2553.         {
  2554.                 IAIObject* pAIObject = GetAISystem()->GetNearestObjectOfTypeInRange(
  2555.                   pPipeUser->GetPos(), m_nObjectType, 0, (m_fRange > 0.001f) ? m_fRange : 50.f);
  2556.  
  2557.                 pPipeUser->SetLastOpResult(GetWeakRef(static_cast<CAIObject*>(pAIObject)));
  2558.                 return pAIObject ? eGOR_SUCCEEDED : eGOR_FAILED;
  2559.         }
  2560.  
  2561.         CAIObject* pAIObject = pPipeUser->GetSpecialAIObject(m_sName, m_fRange);
  2562.  
  2563.         // don't check the range if m_fRange doesn't really represent a range :-/
  2564.         if ((pAIObject != NULL) && (m_fRange > 0.001f) && (strcmp(m_sName.c_str(), "formation_id") != 0))
  2565.         {
  2566.                 // check with m_fRange optional parameter
  2567.                 if ((pPipeUser->GetPos() - pAIObject->GetPos()).GetLengthSquared() > m_fRange * m_fRange)
  2568.                 {
  2569.                         pAIObject = 0;
  2570.                 }
  2571.         }
  2572.  
  2573.         // always set lastOpResult even if pAIObject is NULL!!!
  2574.         pPipeUser->SetLastOpResult(GetWeakRef(pAIObject));
  2575.  
  2576.         CCCPOINT(COPLocate_Execute_A);
  2577.         if (!pAIObject)
  2578.         {
  2579.                 if (m_bWarnIfFailed)
  2580.                 {
  2581.                         if (m_sName == "formation")
  2582.                                 AIWarning("No free formation point for %s", pPipeUser->GetName());
  2583.                         else
  2584.                                 gEnv->pLog->LogError("Failed to locate '%s'", m_sName.c_str());
  2585.                 }
  2586.                 return eGOR_FAILED;
  2587.         }
  2588.  
  2589.         return eGOR_SUCCEEDED;
  2590. }
  2591.  
  2592. EGoalOpResult COPIgnoreAll::Execute(CPipeUser* pPipeUser)
  2593. {
  2594.         pPipeUser->MakeIgnorant(m_bIgnoreAll);
  2595.         return eGOR_DONE;
  2596. }
  2597.  
  2598. COPSignal::COPSignal(const XmlNodeRef& node) :
  2599.         m_nSignalID(1),
  2600.         m_sSignal(node->getAttr("name")),
  2601.         m_cFilter(SIGNALFILTER_SENDER),
  2602.         m_bSent(false),
  2603.         m_iDataValue(0)
  2604. {
  2605.         node->getAttr("id", m_nSignalID);
  2606.         s_xml.GetSignalFilter(node, "filter", m_cFilter);
  2607.         node->getAttr("data", m_iDataValue);
  2608. }
  2609.  
  2610. EGoalOpResult COPSignal::Execute(CPipeUser* pPipeUser)
  2611. {
  2612.         CCCPOINT(COPSignal_Execute);
  2613.  
  2614.         if (m_bSent)
  2615.         {
  2616.                 m_bSent = false;
  2617.                 return eGOR_DONE;
  2618.         }
  2619.  
  2620.         AISignalExtraData* pData = new AISignalExtraData;
  2621.         pData->iValue = m_iDataValue;
  2622.  
  2623.         switch (m_cFilter)
  2624.         {
  2625.         case SIGNALFILTER_SENDER:
  2626.                 pPipeUser->SetSignal(m_nSignalID, m_sSignal.c_str(), pPipeUser->GetEntity(), pData);
  2627.                 m_bSent = true;
  2628.                 return eGOR_IN_PROGRESS;
  2629.  
  2630.         case SIGNALFILTER_LASTOP:
  2631.                 {
  2632.                         CAIObject* pLastOpResult = pPipeUser->m_refLastOpResult.GetAIObject();
  2633.                         if (pLastOpResult)
  2634.                         {
  2635.                                 CAIActor* pOperandActor = pLastOpResult->CastToCAIActor();
  2636.                                 if (pOperandActor)
  2637.                                         pOperandActor->SetSignal(m_nSignalID, m_sSignal.c_str(), pPipeUser->GetEntity(), pData);
  2638.                         }
  2639.                         break;
  2640.                 }
  2641.  
  2642.         default:
  2643.                 // signal to species, group or anyone within comm range
  2644.                 GetAISystem()->SendSignal(m_cFilter, m_nSignalID, m_sSignal.c_str(), pPipeUser, pData);
  2645.         }
  2646.  
  2647.         m_bSent = true;
  2648.         return eGOR_IN_PROGRESS;
  2649. }
  2650.  
  2651. COPScript::COPScript(const string& scriptCode)
  2652. {
  2653.         if (!scriptCode.empty())
  2654.         {
  2655.                 m_scriptCode = scriptCode;
  2656.         }
  2657.         else
  2658.         {
  2659.                 AIError("Goalop Script: Given empty string for Lua code");
  2660.         }
  2661. }
  2662.  
  2663. COPScript::COPScript(const XmlNodeRef& node)
  2664. {
  2665.         string sCode = node->getAttr("code");
  2666.         if (!sCode.empty())
  2667.         {
  2668.                 m_scriptCode = sCode;
  2669.         }
  2670.         else
  2671.         {
  2672.                 AIError("Goalop '%s': Given empty string for Lua code", node->getTag());
  2673.         }
  2674.  
  2675. }
  2676.  
  2677. EGoalOpResult COPScript::Execute(CPipeUser* pPipeUser)
  2678. {
  2679.         CCCPOINT(CCOPScript_Execute);
  2680.  
  2681.         if (!m_scriptCode)
  2682.                 return eGOR_FAILED;
  2683.  
  2684.         bool result = false;
  2685.         IScriptSystem* scriptSystem = gEnv->pScriptSystem;
  2686.  
  2687.         const char* bufferDesc = "Script GoalOp";
  2688.  
  2689.         SmartScriptFunction scriptPtr(scriptSystem,
  2690.                                       scriptSystem->CompileBuffer(m_scriptCode.c_str(), m_scriptCode.length(), bufferDesc));
  2691.  
  2692.         if (!scriptPtr)
  2693.         {
  2694.                 AIError("Goalop Script: Failed to compile Lua code when executing goalop: %s", m_scriptCode.c_str());
  2695.                 return eGOR_FAILED;
  2696.         }
  2697.  
  2698.         // TODO(M谩rcio): Set the function environment here, instead of setting globals
  2699.         if (IEntity* entity = pPipeUser->GetEntity())
  2700.         {
  2701.                 scriptSystem->SetGlobalValue("entity", entity->GetScriptTable());
  2702.  
  2703.                 if (Script::CallReturn(gEnv->pScriptSystem, scriptPtr, result))
  2704.                 {
  2705.                         scriptSystem->SetGlobalToNull("entity");
  2706.                         return result ? eGOR_SUCCEEDED : eGOR_FAILED;
  2707.                 }
  2708.         }
  2709.  
  2710.         scriptSystem->SetGlobalToNull("entity");
  2711.  
  2712.         return eGOR_FAILED;
  2713. }
  2714.  
  2715. COPDeValue::COPDeValue(int nPuppetsAlso, bool bClearDevalued) :
  2716.         m_bDevaluePuppets(nPuppetsAlso != 0),
  2717.         m_bClearDevalued(bClearDevalued)
  2718. {
  2719. }
  2720.  
  2721. COPDeValue::COPDeValue(const XmlNodeRef& node) :
  2722.         m_bDevaluePuppets(s_xml.GetBool(node, "devaluePuppets")),
  2723.         m_bClearDevalued(s_xml.GetBool(node, "clearDevalued"))
  2724. {
  2725. }
  2726.  
  2727. EGoalOpResult COPDeValue::Execute(CPipeUser* pPipeUser)
  2728. {
  2729.         if (m_bClearDevalued)
  2730.                 pPipeUser->ClearDevalued();
  2731.         else if (pPipeUser->GetAttentionTarget())
  2732.         {
  2733.                 if (CPuppet* pPuppet = pPipeUser->CastToCPuppet())
  2734.                         pPuppet->Devalue(pPipeUser->GetAttentionTarget(), m_bDevaluePuppets);
  2735.         }
  2736.         return eGOR_DONE;
  2737. }
  2738.  
  2739. ////////////////////////////////////////////////////////////////////////////////////
  2740.  
  2741. EGoalOpResult COPTacticalPos::Execute(CPipeUser* pPipeUser)
  2742. {
  2743.         EGoalOpResult ret = eGOR_FAILED;
  2744.  
  2745.         CAISystem* pAISystem = GetAISystem();
  2746.  
  2747.         switch (m_state)
  2748.         {
  2749.         case eTPS_QUERY_INIT:
  2750.                 {
  2751.                         // Start an async TPS query
  2752.  
  2753.                         // Store our current hidepos
  2754.                         m_vLastHidePos = pPipeUser->m_CurrentHideObject.GetObjectPos();
  2755.  
  2756.                         // Set up context
  2757.                         QueryContext context;
  2758.                         InitQueryContextFromActor(pPipeUser, context);
  2759.                         m_queryInstance.SetContext(context);
  2760.  
  2761.                         // Start the query
  2762.                         m_queryInstance.Execute(eTPQF_LockResults);
  2763.                         m_state = eTPS_QUERY;
  2764.                 }
  2765.         // Fall through! Status may have changed immediately.
  2766.         case eTPS_QUERY:
  2767.                 {
  2768.                         // Wait for results from the TPS query
  2769.  
  2770.                         ETacticalPointQueryState eTPSState = m_queryInstance.GetStatus();
  2771.                         switch (eTPSState)
  2772.                         {
  2773.                         case eTPSQS_InProgress:
  2774.                                 // Query is still being processed
  2775.                                 {
  2776.                                         ret = eGOR_IN_PROGRESS;
  2777.                                 }
  2778.                                 break;
  2779.                         case eTPSQS_Error:
  2780.                         // Got an error - abort this op.
  2781.                         case eTPSQS_Fail:
  2782.                                 {
  2783.                                         // We found no points so we have nowhere to go - but there were no errors
  2784.                                         Reset(pPipeUser);
  2785.  
  2786.                                         // This doesn't quite make sense if we're going to set a refpoint
  2787.  
  2788.                                         // Wrapping with old methods...
  2789.                                         //pPipeUser->SetInCover(false);
  2790.                                         //pPipeUser->m_CurrentHideObject.Invalidate();
  2791.  
  2792.                                         SendStateSignal(pPipeUser, eTPGOpState_NoPointFound);
  2793.  
  2794.                                         ret = eGOR_FAILED;
  2795.                                 }
  2796.                                 break;
  2797.  
  2798.                         case eTPSQS_Success:
  2799.                                 {
  2800.                                         m_queryInstance.UnlockResults();
  2801.                                         // We found a point successfully, so we can continue with the op
  2802.                                         m_iOptionUsed = m_queryInstance.GetOptionUsed();
  2803.  
  2804.                                         if (m_iOptionUsed < 0)
  2805.                                                 SendStateSignal(pPipeUser, eTPGOpState_NoPointFound);
  2806.                                         else
  2807.                                                 SendStateSignal(pPipeUser, eTPGOpState_PointFound);
  2808.  
  2809.                                         STacticalPointResult point = m_queryInstance.GetBestResult();
  2810.                                         assert(point.IsValid());
  2811.  
  2812.                                         // Wrapping old methods...
  2813.                                         pPipeUser->m_CurrentHideObject.Invalidate();
  2814.                                         switch (m_nReg)
  2815.                                         {
  2816.                                         case AI_REG_REFPOINT: