float CAI_PlaneSolver::CalcProbeDist( float speed ) { // one second or one hull float result = GetLookaheadTime() * speed; if ( result < m_pNpc->GetMoveProbe()->GetHullWidth() ) return m_pNpc->GetMoveProbe()->GetHullWidth(); if ( result > MAX_PROBE_DIST[AIStrongOpt()] ) return MAX_PROBE_DIST[AIStrongOpt()]; return result; }
CAI_PlaneSolver::CAI_PlaneSolver( CAI_BaseNPC *pNpc ) : m_pNpc( pNpc ), m_fSolvedPrev( false ), m_PrevTarget( FLT_MAX, FLT_MAX, FLT_MAX ), m_PrevSolution( 0 ), m_ClosestHaveBeenToCurrent( FLT_MAX ), m_TimeLastProgress( FLT_MAX ), m_fCannotSolveCurrent( false ), m_RefreshSamplesTimer( PLANE_SOLVER_THINK_FREQUENCY[AIStrongOpt()] - 0.05 ) { }
bool CAI_LocalNavigator::MoveCalcDirect( AILocalMoveGoal_t *pMoveGoal, bool bOnlyCurThink, float *pDistClear, AIMoveResult_t *pResult ) { AI_PROFILE_SCOPE(CAI_LocalNavigator_MoveCalcDirect); bool bRetVal = false; if ( pMoveGoal->speed ) { CAI_Motor *pMotor = GetOuter()->GetMotor(); float minCheckDist = pMotor->MinCheckDist(); float probeDist = m_pPlaneSolver->CalcProbeDist( pMoveGoal->speed ); // having this match steering allows one fewer traces float checkDist = MAX( minCheckDist, probeDist ); float checkStepDist = MAX( 16.0, probeDist * 0.5 ); if ( pMoveGoal->flags & ( AILMG_TARGET_IS_TRANSITION | AILMG_TARGET_IS_GOAL ) ) { // clamp checkDist to be no farther than MAX distance to goal checkDist = MIN( checkDist, pMoveGoal->maxDist ); } if ( checkDist <= 0.0 ) { *pResult = AIMR_OK; return true; } float moveThisInterval = pMotor->CalcIntervalMove(); bool bExpectingArrival = (moveThisInterval >= checkDist); if ( !m_FullDirectTimer.Expired() ) { if ( !m_fLastWasClear || ( !VectorsAreEqual(pMoveGoal->target, m_LastMoveGoal.target, 0.1) || !VectorsAreEqual(pMoveGoal->dir, m_LastMoveGoal.dir, 0.1) ) || bExpectingArrival ) { m_FullDirectTimer.Force(); } } if ( bOnlyCurThink ) // Outer code claims to have done a validation (probably a simplify operation) { m_FullDirectTimer.Set( TIME_DELAY_FULL_DIRECT_PROBE[AIStrongOpt()] ); } // First, check the probable move for this cycle bool bTraceClear = true; Vector testPos; if ( !bExpectingArrival ) { testPos = GetLocalOrigin() + pMoveGoal->dir * moveThisInterval; bTraceClear = GetMoveProbe()->MoveLimit( pMoveGoal->navType, GetLocalOrigin(), testPos, GetOuter()->GetAITraceMask(), pMoveGoal->pMoveTarget, 100.0, ( pMoveGoal->navType == NAV_GROUND ) ? AIMLF_2D : AIMLF_DEFAULT, &pMoveGoal->directTrace ); if ( !bTraceClear ) { // Adjust probe top match expected probe dist (relied on later in process) pMoveGoal->directTrace.flDistObstructed = (checkDist - moveThisInterval) + pMoveGoal->directTrace.flDistObstructed; } if ( !IsRetail() && ai_debug_directnavprobe.GetBool() ) { if ( !bTraceClear ) { DevMsg( GetOuter(), "Close obstruction %f\n", checkDist - pMoveGoal->directTrace.flDistObstructed ); NDebugOverlay::Line( WorldSpaceCenter(), Vector( testPos.x, testPos.y, WorldSpaceCenter().z ), 255, 0, 0, false, 0.1 ); if ( pMoveGoal->directTrace.pObstruction ) NDebugOverlay::Line( WorldSpaceCenter(), pMoveGoal->directTrace.pObstruction->WorldSpaceCenter(), 255, 0, 255, false, 0.1 ); } else { NDebugOverlay::Line( WorldSpaceCenter(), Vector( testPos.x, testPos.y, WorldSpaceCenter().z ), 0, 255, 0, false, 0.1 ); } } pMoveGoal->thinkTrace = pMoveGoal->directTrace; } // Now project out for future obstructions if ( bTraceClear ) { if ( m_FullDirectTimer.Expired() ) { testPos = GetLocalOrigin() + pMoveGoal->dir * checkDist; float checkStepPct = (checkStepDist / checkDist) * 100.0; if ( checkStepPct > 100.0 ) checkStepPct = 100.0; bTraceClear = GetMoveProbe()->MoveLimit( pMoveGoal->navType, GetLocalOrigin(), testPos, GetOuter()->GetAITraceMask(), pMoveGoal->pMoveTarget, checkStepPct, ( pMoveGoal->navType == NAV_GROUND ) ? AIMLF_2D : AIMLF_DEFAULT, &pMoveGoal->directTrace ); if ( bExpectingArrival ) pMoveGoal->thinkTrace = pMoveGoal->directTrace; if (ai_debug_directnavprobe.GetBool() ) { if ( !bTraceClear ) { NDebugOverlay::Line( GetOuter()->EyePosition(), Vector( testPos.x, testPos.y, GetOuter()->EyePosition().z ), 255, 0, 0, false, 0.1 ); DevMsg( GetOuter(), "Obstruction %f\n", checkDist - pMoveGoal->directTrace.flDistObstructed ); } else { NDebugOverlay::Line( GetOuter()->EyePosition(), Vector( testPos.x, testPos.y, GetOuter()->EyePosition().z ), 0, 255, 0, false, 0.1 ); DevMsg( GetOuter(), "No obstruction\n" ); } } } else { if ( ai_debug_directnavprobe.GetBool() ) DevMsg( GetOuter(), "No obstruction (Near probe only)\n" ); } } pMoveGoal->bHasTraced = true; float distClear = checkDist - pMoveGoal->directTrace.flDistObstructed; if (distClear < 0.001) distClear = 0; if ( bTraceClear ) { *pResult = AIMR_OK; bRetVal = true; m_fLastWasClear = true; } else if ( ( pMoveGoal->flags & ( AILMG_TARGET_IS_TRANSITION | AILMG_TARGET_IS_GOAL ) ) && pMoveGoal->maxDist < distClear ) { *pResult = AIMR_OK; bRetVal = true; m_fLastWasClear = true; } else { *pDistClear = distClear; m_fLastWasClear = false; } } else { // Should never end up in this function with speed of zero. Probably an activity problem. *pResult = AIMR_ILLEGAL; bRetVal = true; } m_LastMoveGoal = *pMoveGoal; if ( bRetVal && m_FullDirectTimer.Expired() ) m_FullDirectTimer.Set( TIME_DELAY_FULL_DIRECT_PROBE[AIStrongOpt()] ); return bRetVal; }
AI_SuggestorResult_t CAI_PlaneSolver::GenerateObstacleSuggestions( const AILocalMoveGoal_t &goal, const AIMoveTrace_t &directTrace, float distClear, float probeDist, float degreesToProbe, int nProbes ) { Assert( nProbes % 2 == 1 ); PLANESOLVER_PROFILE_SCOPE( CAI_PlaneSolver_GenerateObstacleSuggestions ); AI_SuggestorResult_t seekResult = SR_NONE; bool fNewTarget = ( !m_fSolvedPrev || m_PrevTarget != goal.target ); if ( fNewTarget ) m_RefreshSamplesTimer.Force(); if ( PLANE_SOLVER_THINK_FREQUENCY[AIStrongOpt()] == 0.0 || m_RefreshSamplesTimer.Expired() ) { m_Solver.ClearRegulations(); if ( !ProbeForNpcs() ) GenerateObstacleNpcs( goal, probeDist ); if ( GenerateCircleObstacleSuggestions( goal, probeDist ) ) seekResult = SR_OK; float spanPerProbe = degreesToProbe / nProbes; int nSideProbes = (nProbes - 1) / 2; float yawGoalDir = UTIL_VecToYaw( goal.dir ); Vector probeTarget; AIMoveTrace_t moveTrace; int i; // Generate suggestion from direct trace, or probe if direct trace doesn't match if ( fabs( probeDist - ( distClear + directTrace.flDistObstructed ) ) < 0.1 && ( ProbeForNpcs() || directTrace.fStatus != AIMR_BLOCKED_NPC ) ) { if ( directTrace.fStatus != AIMR_OK ) { seekResult = SR_OK; GenerateSuggestionFromTrace( goal, directTrace, probeDist, yawGoalDir, spanPerProbe, 0 ); } } else if ( GenerateObstacleSuggestion( goal, yawGoalDir, probeDist, spanPerProbe, 0 ) == SR_OK ) { seekResult = SR_OK; } // Scan left. Note that in the left and right scan, the algorithm stops as soon // as there is a clear path. This is an optimization in anticipation of the // behavior of the underlying solver. This will break more often the higher // PLANE_SOLVER_THINK_FREQUENCY becomes bool foundClear = false; for ( i = 1; i <= nSideProbes; i++ ) { if ( !foundClear ) { AI_SuggestorResult_t curSeekResult = GenerateObstacleSuggestion( goal, yawGoalDir, probeDist, spanPerProbe, i ); if ( curSeekResult == SR_OK ) { seekResult = SR_OK; } else foundClear = true; } else { float ignored; float arcCenter; CalcYawsFromOffset( yawGoalDir, spanPerProbe, i, &ignored, &arcCenter ); m_Solver.AddRegulation( AI_MoveSuggestion_t( AIMST_NO_KNOWLEDGE, 1, arcCenter, spanPerProbe ) ); } } // Scan right foundClear = false; for ( i = -1; i >= -nSideProbes; i-- ) { if ( !foundClear ) { AI_SuggestorResult_t curSeekResult = GenerateObstacleSuggestion( goal, yawGoalDir, probeDist, spanPerProbe, i ); if ( curSeekResult == SR_OK ) { seekResult = SR_OK; } else foundClear = true; } else { float ignored; float arcCenter; CalcYawsFromOffset( yawGoalDir, spanPerProbe, i, &ignored, &arcCenter ); m_Solver.AddRegulation( AI_MoveSuggestion_t( AIMST_NO_KNOWLEDGE, 1, arcCenter, spanPerProbe ) ); } } if ( seekResult == SR_OK ) { float arcCenter = yawGoalDir - 180; if ( arcCenter < 0 ) arcCenter += 360; // Since these are not sampled every think, place a negative arc in all directions not sampled m_Solver.AddRegulation( AI_MoveSuggestion_t( AIMST_NO_KNOWLEDGE, 1, arcCenter, 360 - degreesToProbe ) ); } m_RefreshSamplesTimer.Reset( PLANE_SOLVER_THINK_FREQUENCY[AIStrongOpt()] ); } else if ( m_Solver.HaveRegulations() ) seekResult = SR_OK; return seekResult; }
#include "cbase.h" #include "ai_localnavigator.h" #include "ai_basenpc.h" #include "ai_planesolver.h" #include "ai_moveprobe.h" #include "ai_motor.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" ConVar ai_debug_directnavprobe("ai_debug_directnavprobe", "0"); const float TIME_DELAY_FULL_DIRECT_PROBE = ( !AIStrongOpt() ) ? 0.25 : 0.35; //----------------------------------------------------------------------------- BEGIN_SIMPLE_DATADESC(CAI_LocalNavigator) // m_fLastWasClear (not saved) // m_LastMoveGoal (not saved) // m_FullDirectTimer (not saved) // m_pPlaneSolver (not saved) // m_pMoveProbe (not saved) END_DATADESC(); //------------------------------------- CAI_LocalNavigator::CAI_LocalNavigator(CAI_BaseNPC *pOuter) : CAI_Component( pOuter ) {