// Draw a portion of our current path for debugging. void CCSBot::DrawPath() { if (!HasPath()) return; for (int i = 1; i < m_pathLength; i++) { UTIL_DrawBeamPoints(m_path[i - 1].pos, m_path[i].pos, 2, 255, 75, 0); } Vector close; if (FindOurPositionOnPath(&close, true) >= 0) { UTIL_DrawBeamPoints(close + Vector(0, 0, 25), close, 1, 0, 255, 0); UTIL_DrawBeamPoints(close + Vector(25, 0, 0), close + Vector(-25, 0, 0), 1, 0, 255, 0); UTIL_DrawBeamPoints(close + Vector(0, 25, 0), close + Vector(0, -25, 0), 1, 0, 255, 0); } }
/** * Compute a point a fixed distance ahead along our path. * Returns path index just after point. */ int CNavPathFollower::FindPathPoint( float aheadRange, Vector *point, int *prevIndex ) { // find path index just past aheadRange int afterIndex; // finds the closest point on local area of path, and returns the path index just prior to it Vector close; int startIndex = FindOurPositionOnPath( &close, true ); if (prevIndex) *prevIndex = startIndex; if (startIndex <= 0) { // went off the end of the path // or next point in path is unwalkable (ie: jump-down) // keep same point return m_segmentIndex; } // if we are crouching, just follow the path exactly if (m_improv->IsCrouching()) { // we want to move to the immediately next point along the path from where we are now int index = startIndex+1; if (index >= m_path->GetSegmentCount()) index = m_path->GetSegmentCount()-1; *point = (*m_path)[ index ]->pos; // if we are very close to the next point in the path, skip ahead to the next one to avoid wiggling // we must do a 2D check here, in case the goal point is floating in space due to jump down, etc const float closeEpsilon = 20.0f; // 10 while ((*point - close).Make2D().IsLengthLessThan( closeEpsilon )) { ++index; if (index >= m_path->GetSegmentCount()) { index = m_path->GetSegmentCount()-1; break; } *point = (*m_path)[ index ]->pos; } return index; } // make sure we use a node a minimum distance ahead of us, to avoid wiggling while (startIndex < m_path->GetSegmentCount()-1) { Vector pos = (*m_path)[ startIndex+1 ]->pos; // we must do a 2D check here, in case the goal point is floating in space due to jump down, etc const float closeEpsilon = 20.0f; if ((pos - close).Make2D().IsLengthLessThan( closeEpsilon )) { ++startIndex; } else { break; } } // if we hit a ladder or jump area, must stop (dont use ladder behind us) if (startIndex > m_segmentIndex && startIndex < m_path->GetSegmentCount() && ((*m_path)[ startIndex ]->ladder || (*m_path)[ startIndex ]->area->GetAttributes() & NAV_JUMP)) { *point = (*m_path)[ startIndex ]->pos; return startIndex; } // we need the point just *ahead* of us ++startIndex; if (startIndex >= m_path->GetSegmentCount()) startIndex = m_path->GetSegmentCount()-1; // if we hit a ladder or jump area, must stop if (startIndex < m_path->GetSegmentCount() && ((*m_path)[ startIndex ]->ladder || (*m_path)[ startIndex ]->area->GetAttributes() & NAV_JUMP)) { *point = (*m_path)[ startIndex ]->pos; return startIndex; } // note direction of path segment we are standing on Vector initDir = (*m_path)[ startIndex ]->pos - (*m_path)[ startIndex-1 ]->pos; initDir.NormalizeInPlace(); Vector feet = m_improv->GetFeet(); Vector eyes = m_improv->GetEyes(); float rangeSoFar = 0; // this flag is true if our ahead point is visible bool visible = true; Vector prevDir = initDir; // step along the path until we pass aheadRange bool isCorner = false; int i; for( i=startIndex; i<m_path->GetSegmentCount(); ++i ) { Vector pos = (*m_path)[i]->pos; Vector to = pos - (*m_path)[i-1]->pos; Vector dir = to.Normalize(); // don't allow path to double-back from our starting direction (going upstairs, down curved passages, etc) if (DotProduct( dir, initDir ) < 0.0f) // -0.25f { --i; break; } // if the path turns a corner, we want to move towards the corner, not into the wall/stairs/etc if (DotProduct( dir, prevDir ) < 0.5f) { isCorner = true; --i; break; } prevDir = dir; // don't use points we cant see Vector probe = pos + Vector( 0, 0, HalfHumanHeight ); if (!IsWalkableTraceLineClear( eyes, probe, WALK_THRU_BREAKABLES )) { // presumably, the previous point is visible, so we will interpolate visible = false; break; } // if we encounter a ladder or jump area, we must stop if (i < m_path->GetSegmentCount() && ((*m_path)[ i ]->ladder || (*m_path)[ i ]->area->GetAttributes() & NAV_JUMP)) break; // Check straight-line path from our current position to this position // Test for un-jumpable height change, or unrecoverable fall //if (!IsStraightLinePathWalkable( &pos )) //{ // --i; // break; //} Vector along = (i == startIndex) ? (pos - feet) : (pos - (*m_path)[i-1]->pos); rangeSoFar += along.Length2D(); // stop if we have gone farther than aheadRange if (rangeSoFar >= aheadRange) break; } if (i < startIndex) afterIndex = startIndex; else if (i < m_path->GetSegmentCount()) afterIndex = i; else afterIndex = m_path->GetSegmentCount()-1; // compute point on the path at aheadRange if (afterIndex == 0) { *point = (*m_path)[0]->pos; } else { // interpolate point along path segment const Vector *afterPoint = &(*m_path)[ afterIndex ]->pos; const Vector *beforePoint = &(*m_path)[ afterIndex-1 ]->pos; Vector to = *afterPoint - *beforePoint; float length = to.Length2D(); float t = 1.0f - ((rangeSoFar - aheadRange) / length); if (t < 0.0f) t = 0.0f; else if (t > 1.0f) t = 1.0f; *point = *beforePoint + t * to; // if afterPoint wasn't visible, slide point backwards towards beforePoint until it is if (!visible) { const float sightStepSize = 25.0f; float dt = sightStepSize / length; Vector probe = *point + Vector( 0, 0, HalfHumanHeight ); while( t > 0.0f && !IsWalkableTraceLineClear( eyes, probe, WALK_THRU_BREAKABLES ) ) { t -= dt; *point = *beforePoint + t * to; } if (t <= 0.0f) *point = *beforePoint; } } // if position found is too close to us, or behind us, force it farther down the path so we don't stop and wiggle if (!isCorner) { const float epsilon = 50.0f; Vector2D toPoint; Vector2D centroid( m_improv->GetCentroid().x, m_improv->GetCentroid().y ); toPoint.x = point->x - centroid.x; toPoint.y = point->y - centroid.y; if (DotProduct( toPoint, initDir.Make2D() ) < 0.0f || toPoint.IsLengthLessThan( epsilon )) { int i; for( i=startIndex; i<m_path->GetSegmentCount(); ++i ) { toPoint.x = (*m_path)[i]->pos.x - centroid.x; toPoint.y = (*m_path)[i]->pos.y - centroid.y; if ((*m_path)[i]->ladder || (*m_path)[i]->area->GetAttributes() & NAV_JUMP || toPoint.IsLengthGreaterThan( epsilon )) { *point = (*m_path)[i]->pos; startIndex = i; break; } } if (i == m_path->GetSegmentCount()) { *point = m_path->GetEndpoint(); startIndex = m_path->GetSegmentCount()-1; } } } // m_pathIndex should always be the next point on the path, even if we're not moving directly towards it if (startIndex < m_path->GetSegmentCount()) return startIndex; return m_path->GetSegmentCount()-1; }