inline void DrawAxes(const Vector &origin, int red, int green, int blue) { float size = 10; if (cv_hostage_debug.value != 1.0f) return; UTIL_DrawBeamPoints(origin - Vector(size, 0, 0), origin + Vector(size, 0, 0), 2, red, green, blue); UTIL_DrawBeamPoints(origin - Vector(0, size, 0), origin + Vector(0, size, 0), 2, red, green, blue); UTIL_DrawBeamPoints(origin + Vector(0, 0, size), origin - Vector(0, 0, size), 2, red, green, blue); }
void CCSBot::DrawApproachPoints() { for (int i = 0; i < m_approachPointCount; ++i) { UTIL_DrawBeamPoints(m_approachPoint[i], m_approachPoint[i] + Vector(0, 0, 50), 3, 0, 255, 255); } }
void CHostage::Wiggle(void) { TraceResult tr; Vector vec = Vector(0, 0, 0); Vector wiggle_directions[8] = { Vector(50, 0, 0), Vector(-50, 0, 0), Vector(0, 50, 0), Vector(0, -50, 0), Vector(50, 50, 0), Vector(50, -50, 0), Vector(-50, 50, 0), Vector(-50, -50, 0) }; for (int i = 0; i < 8; i++) { if (m_LocalNav->PathTraversable(pev->origin, pev->origin + wiggle_directions[i], TRUE) == TRAVERSABLE_NO) vec = vec - wiggle_directions[i]; } Vector vecSrc, vecDest; vec = vec + Vector(RANDOM_FLOAT(-3, 3), RANDOM_FLOAT(-3, 3), 0); pev->velocity = pev->velocity + (vec.Normalize() * 100); vecSrc = pev->origin; vecDest = vecSrc + (pev->velocity.Normalize() * 500); UTIL_DrawBeamPoints(vecSrc, vecDest, 10, 0, 0, 255); }
void CHostage::Touch(CBaseEntity *pOther) { if (m_improv) return; if (pOther->IsPlayer() == TRUE) { if (((CBasePlayer *)pOther)->m_iTeam != TEAM_CT) return; } else { if (!FClassnameIs(pOther->pev, "hostage_entity")) return; } Vector vecSrc, vecDest; vecDest = (pev->origin - pOther->pev->origin); vecDest.z = pev->origin.z - pev->origin.z; vecDest = vecDest.Normalize() * 50; pev->velocity = pev->velocity + vecDest; vecSrc = pev->origin; vecDest = vecSrc + (pev->velocity.Normalize() * 500); UTIL_DrawBeamPoints(vecSrc, vecDest, 10, 0, 255, 0); }
/** * Draw the path for debugging. */ void CNavPath::Draw( void ) { if (!IsValid()) return; for( int i=1; i<m_segmentCount; ++i ) UTIL_DrawBeamPoints( m_path[i-1].pos + Vector( 0, 0, HalfHumanHeight ), m_path[i].pos + Vector( 0, 0, HalfHumanHeight ), 2, 255, 75, 0 ); }
// 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); } }
void CHostage::MoveToward(Vector &vecLoc) { int nFwdMove; Vector vecFwd; Vector vecbigDest; Vector vecMove; CBaseEntity *pFollowing; Vector vecAng; float flDist; pFollowing = GetClassPtr((CBaseEntity *)m_hTargetEnt->pev); vecMove = vecLoc - pev->origin; vecAng = UTIL_VecToAngles(vecMove); vecAng = Vector(0, vecAng.y, 0); UTIL_MakeVectorsPrivate(vecAng, vecFwd, NULL, NULL); if ((vecFwd * m_LocalNav->s_flStepSize).Length2D() <= (vecLoc - pev->origin).Length2D()) flDist = (vecFwd * m_LocalNav->s_flStepSize).Length2D(); else flDist = (vecLoc - pev->origin).Length2D(); vecbigDest = pev->origin + (vecFwd * flDist); nFwdMove = m_LocalNav->PathTraversable(pev->origin, vecbigDest, FALSE); if (nFwdMove != TRAVERSABLE_NO) { vecbigDest = pFollowing->pev->origin; vecbigDest.z += pFollowing->pev->mins.z; if (FBitSet(pev->flags, FL_ONGROUND)) { flDist = (vecbigDest - pev->origin).Length(); if (flDist >= 110) { if (flDist >= 250) flDist = 400; else flDist = 300; } } else flDist = 250; pev->velocity.x = vecFwd.x * flDist; pev->velocity.y = vecFwd.y * flDist; UTIL_DrawBeamPoints(pev->origin, pev->origin + (pev->velocity.Normalize() * 500), 10, 255, 0, 0); if (nFwdMove != TRAVERSABLE_STEP && nFwdMove == TRAVERSABLE_STEPJUMPABLE) { if (FBitSet(pev->flags, FL_ONGROUND)) pev->velocity.z = 270; } } }
void CHostageImprov::__MAKE_VHOOK(OnTouch)(CBaseEntity *other) { const char *classname; Vector2D to; const float pushForce = 20.0f; classname = STRING(other->pev->classname); if (cv_hostage_debug.value != 0.0) { CONSOLE_ECHO("%5.1f: Hostage hit '%s'\n", gpGlobals->time, classname); } m_collisionTimer.Start(); if (FStrEq(classname, "worldspawn")) { const float lookAheadRange = 30.0f; float ground; Vector normal = Vector(0, 0, 1); Vector alongFloor; TraceResult result; bool isStep = false; UTIL_MakeVectors(m_hostage->pev->angles); if (!GetSimpleGroundHeightWithFloor(&GetEyes(), &ground, &normal)) return; if (cv_hostage_debug.value < 0.0) { UTIL_DrawBeamPoints(GetFeet() + normal * 50, GetFeet(), 2, 255, 255, 0); } alongFloor = CrossProduct(normal, gpGlobals->v_right); Vector pos = alongFloor * lookAheadRange; for (float_precision offset = 1.0f; offset <= 18.0f; offset += 3.0f) { Vector vecStart = GetFeet(); vecStart.z += offset; UTIL_TraceLine(vecStart, vecStart + pos, ignore_monsters, dont_ignore_glass, m_hostage->pev->pContainingEntity, &result); if (result.flFraction < 1.0f && result.vecPlaneNormal.z < MaxUnitZSlope) { isStep = true; break; } } if (isStep) { float stepAheadGround = pos.z; Vector stepAheadNormal = Vector(0, 0, stepAheadGround); m_inhibitObstacleAvoidance.Start(0.5); for (float range = 1.0f; range <= 30.5f; range += 5.0f) { Vector stepAhead = GetFeet() + alongFloor * range; stepAhead.z = GetEyes().z; if (GetSimpleGroundHeightWithFloor(&stepAhead, &stepAheadGround, &stepAheadNormal)) { float dz = stepAheadGround - GetFeet().z; if (dz > 0.0f && dz < 18.0f) { m_hostage->pev->origin.z = stepAheadGround + 3.0f; break; } } } } else if (!IsMoving() && !IsUsingLadder()) { bool isSeam = false; const float checkSeamRange = 50.0f; Vector posBehind; posBehind = GetEyes() - alongFloor * checkSeamRange; UTIL_TraceLine(posBehind, posBehind - Vector(0, 0, 9999), ignore_monsters, dont_ignore_glass, m_hostage->pev->pContainingEntity, &result); if (result.flFraction < 1.0f && DotProduct(result.vecPlaneNormal, normal) < 1.0f) { isSeam = true; } else { Vector posAhead = GetEyes() + alongFloor * checkSeamRange; UTIL_TraceLine(posAhead, posAhead - Vector(0, 0, 9999), ignore_monsters, dont_ignore_glass, m_hostage->pev->pContainingEntity, &result); if (result.flFraction < 1.0f && DotProduct(result.vecPlaneNormal, normal) < 1.0f) isSeam = true; } if (isSeam) { if (cv_hostage_debug.value != 0.0) { CONSOLE_ECHO("Hostage stuck on seam.\n"); } const float nudge = 3.0f; m_hostage->pev->origin.z += nudge; } } } else if (FStrEq(classname, "func_breakable")) { other->TakeDamage(m_hostage->pev, m_hostage->pev, 9999.9, DMG_BULLET); } else if (other->IsPlayer() || FClassnameIs(other->pev, "hostage_entity")) { to = (m_hostage->pev->origin - other->pev->origin).Make2D(); to.NormalizeInPlace(); m_vel.x += to.x * pushForce; m_vel.y += to.y * pushForce; } }
/** * Do reflex avoidance movements if our "feelers" are touched * @todo Parameterize feeler spacing */ void CNavPathFollower::FeelerReflexAdjustment( Vector *goalPosition, float height ) { // if we are in a "precise" area, do not do feeler adjustments if (m_improv->GetLastKnownArea() && m_improv->GetLastKnownArea()->GetAttributes() & NAV_PRECISE) return; Vector dir( BotCOS( m_improv->GetMoveAngle() ), BotSIN( m_improv->GetMoveAngle() ), 0.0f ); dir.z = 0.0f; dir.NormalizeInPlace(); Vector lat( -dir.y, dir.x, 0.0f ); const float feelerOffset = (m_improv->IsCrouching()) ? 20.0f : 25.0f; // 15, 20 const float feelerLengthRun = 50.0f; // 100 - too long for tight hallways (cs_747) const float feelerLengthWalk = 30.0f; const float feelerHeight = (height > 0.0f) ? height : StepHeight + 0.1f; // if obstacle is lower than StepHeight, we'll walk right over it float feelerLength = (m_improv->IsRunning()) ? feelerLengthRun : feelerLengthWalk; feelerLength = (m_improv->IsCrouching()) ? 20.0f : feelerLength; // // Feelers must follow floor slope // float ground; Vector normal; if (m_improv->GetSimpleGroundHeightWithFloor( &m_improv->GetEyes(), &ground, &normal ) == false) return; // get forward vector along floor dir = CrossProduct( lat, normal ); // correct the sideways vector lat = CrossProduct( dir, normal ); Vector feet = m_improv->GetFeet(); feet.z += feelerHeight; Vector from = feet + feelerOffset * lat; Vector to = from + feelerLength * dir; bool leftClear = IsWalkableTraceLineClear( from, to, WALK_THRU_DOORS | WALK_THRU_BREAKABLES ); // draw debug beams if (m_isDebug) { if (leftClear) UTIL_DrawBeamPoints( from, to, 1, 0, 255, 0 ); else UTIL_DrawBeamPoints( from, to, 1, 255, 0, 0 ); } from = feet - feelerOffset * lat; to = from + feelerLength * dir; bool rightClear = IsWalkableTraceLineClear( from, to, WALK_THRU_DOORS | WALK_THRU_BREAKABLES ); // draw debug beams if (m_isDebug) { if (rightClear) UTIL_DrawBeamPoints( from, to, 1, 0, 255, 0 ); else UTIL_DrawBeamPoints( from, to, 1, 255, 0, 0 ); } const float avoidRange = (m_improv->IsCrouching()) ? 150.0f : 300.0f; if (!rightClear) { if (leftClear) { // right hit, left clear - veer left *goalPosition = *goalPosition + avoidRange * lat; //*goalPosition = m_improv->GetFeet() + avoidRange * lat; //m_improv->StrafeLeft(); } } else if (!leftClear) { // right clear, left hit - veer right *goalPosition = *goalPosition - avoidRange * lat; //*goalPosition = m_improv->GetFeet() - avoidRange * lat; //m_improv->StrafeRight(); } }
/** * Move improv along path */ void CNavPathFollower::Update( float deltaT, bool avoidObstacles ) { if (m_path == NULL || m_path->IsValid() == false) return; const CNavPath::PathSegment *node = (*m_path)[ m_segmentIndex ]; if (node == NULL) { m_improv->OnMoveToFailure( m_path->GetEndpoint(), IImprovEvent::FAIL_INVALID_PATH ); m_path->Invalidate(); return; } // handle ladders if (node->ladder) { const Vector *approachPos = NULL; const Vector *departPos = NULL; if (m_segmentIndex) approachPos = &(*m_path)[ m_segmentIndex-1 ]->pos; if (m_segmentIndex < m_path->GetSegmentCount()-1) departPos = &(*m_path)[ m_segmentIndex+1 ]->pos; if (!m_isLadderStarted) { // set up ladder movement m_improv->StartLadder( node->ladder, node->how, approachPos, departPos ); m_isLadderStarted = true; } // move improv along ladder if (m_improv->TraverseLadder( node->ladder, node->how, approachPos, departPos, deltaT )) { // completed ladder ++m_segmentIndex; } return; } // reset ladder init flag m_isLadderStarted = false; // // Check if we reached the end of the path // const float closeRange = 20.0f; if ((m_improv->GetFeet() - node->pos).IsLengthLessThan( closeRange )) { ++m_segmentIndex; if (m_segmentIndex >= m_path->GetSegmentCount()) { m_improv->OnMoveToSuccess( m_path->GetEndpoint() ); m_path->Invalidate(); return; } } m_goal = node->pos; const float aheadRange = 300.0f; m_segmentIndex = FindPathPoint( aheadRange, &m_goal, &m_behindIndex ); if (m_segmentIndex >= m_path->GetSegmentCount()) m_segmentIndex = m_path->GetSegmentCount()-1; bool isApproachingJumpArea = false; // // Crouching // if (!m_improv->IsUsingLadder()) { // because hostage crouching is not really supported by the engine, // if we are standing in a crouch area, we must crouch to avoid collisions if (m_improv->GetLastKnownArea() && m_improv->GetLastKnownArea()->GetAttributes() & NAV_CROUCH && !(m_improv->GetLastKnownArea()->GetAttributes() & NAV_JUMP)) { m_improv->Crouch(); } // if we are approaching a crouch area, crouch // if there are no crouch areas coming up, stand const float crouchRange = 50.0f; bool didCrouch = false; for( int i=m_segmentIndex; i<m_path->GetSegmentCount(); ++i ) { const CNavArea *to = (*m_path)[i]->area; // if there is a jump area on the way to the crouch area, don't crouch as it messes up the jump if (to->GetAttributes() & NAV_JUMP) { isApproachingJumpArea = true; break; } Vector close; to->GetClosestPointOnArea( &m_improv->GetCentroid(), &close ); if ((close - m_improv->GetFeet()).Make2D().IsLengthGreaterThan( crouchRange )) break; if (to->GetAttributes() & NAV_CROUCH) { m_improv->Crouch(); didCrouch = true; break; } } if (!didCrouch && !m_improv->IsJumping()) { // no crouch areas coming up m_improv->StandUp(); } } // end crouching logic if (m_isDebug) { m_path->Draw(); UTIL_DrawBeamPoints( m_improv->GetCentroid(), m_goal + Vector( 0, 0, StepHeight ), 1, 255, 0, 255 ); UTIL_DrawBeamPoints( m_goal + Vector( 0, 0, StepHeight ), m_improv->GetCentroid(), 1, 255, 0, 255 ); } // check if improv becomes stuck m_stuckMonitor.Update( m_improv ); // if improv has been stuck for too long, give up const float giveUpTime = 2.0f; if (m_stuckMonitor.GetDuration() > giveUpTime) { m_improv->OnMoveToFailure( m_path->GetEndpoint(), IImprovEvent::FAIL_STUCK ); m_path->Invalidate(); return; } // if our goal is high above us, we must have fallen if (m_goal.z - m_improv->GetFeet().z > JumpCrouchHeight) { const float closeRange = 75.0f; Vector2D to( m_improv->GetFeet().x - m_goal.x, m_improv->GetFeet().y - m_goal.y ); if (to.IsLengthLessThan( closeRange )) { // we can't reach the goal position // check if we can reach the next node, in case this was a "jump down" situation const CNavPath::PathSegment *nextNode = (*m_path)[ m_behindIndex+1 ]; if (m_behindIndex >=0 && nextNode) { if (nextNode->pos.z - m_improv->GetFeet().z > JumpCrouchHeight) { // the next node is too high, too - we really did fall of the path m_improv->OnMoveToFailure( m_path->GetEndpoint(), IImprovEvent::FAIL_FELL_OFF ); m_path->Invalidate(); return; } } else { // fell trying to get to the last node in the path m_improv->OnMoveToFailure( m_path->GetEndpoint(), IImprovEvent::FAIL_FELL_OFF ); m_path->Invalidate(); return; } } } // avoid small obstacles if (avoidObstacles && !isApproachingJumpArea && !m_improv->IsJumping() && m_segmentIndex < m_path->GetSegmentCount()-1) { FeelerReflexAdjustment( &m_goal ); // currently, this is only used for hostages, and their collision physics stinks // do more feeler checks to avoid short obstacles /* const float inc = 0.25f; for( float t = 0.5f; t < 1.0f; t += inc ) { FeelerReflexAdjustment( &m_goal, t * StepHeight ); } */ } // move improv along path m_improv->TrackPath( m_goal, deltaT ); }
// Move along the path. Return false if end of path reached. CCSBot::PathResult CCSBot::UpdatePathMovement(bool allowSpeedChange) { if (m_pathLength == 0) return PATH_FAILURE; if (cv_bot_walk.value != 0.0f) Walk(); // If we are navigating a ladder, it overrides all other path movement until complete if (UpdateLadderMovement()) return PROGRESSING; // ladder failure can destroy the path if (m_pathLength == 0) return PATH_FAILURE; // we are not supposed to be on a ladder - if we are, jump off if (IsOnLadder()) Jump(MUST_JUMP); assert(m_pathIndex < m_pathLength); // Check if reached the end of the path bool nearEndOfPath = false; if (m_pathIndex >= m_pathLength - 1) { Vector toEnd(pev->origin.x, pev->origin.y, GetFeetZ()); Vector d = GetPathEndpoint() - toEnd; // can't use 2D because path end may be below us (jump down) const float walkRange = 200.0f; // walk as we get close to the goal position to ensure we hit it if (d.IsLengthLessThan(walkRange)) { // don't walk if crouching - too slow if (allowSpeedChange && !IsCrouching()) Walk(); // note if we are near the end of the path const float nearEndRange = 50.0f; if (d.IsLengthLessThan(nearEndRange)) nearEndOfPath = true; const float closeEpsilon = 20.0f; if (d.IsLengthLessThan(closeEpsilon)) { // reached goal position - path complete DestroyPath(); // TODO: We should push and pop walk state here, in case we want to continue walking after reaching goal if (allowSpeedChange) Run(); return END_OF_PATH; } } } // To keep us moving smoothly, we will move towards // a point farther ahead of us down our path. int prevIndex = 0; // closest index on path just prior to where we are now const float aheadRange = 300.0f; int newIndex = FindPathPoint(aheadRange, &m_goalPosition, &prevIndex); // BOTPORT: Why is prevIndex sometimes -1? if (prevIndex < 0) prevIndex = 0; // if goal position is near to us, we must be about to go around a corner - so look ahead! const float nearCornerRange = 100.0f; if (m_pathIndex < m_pathLength - 1 && (m_goalPosition - pev->origin).IsLengthLessThan(nearCornerRange)) { ClearLookAt(); InhibitLookAround(0.5f); } // if we moved to a new node on the path, setup movement if (newIndex > m_pathIndex) { SetPathIndex(newIndex); } if (!IsUsingLadder()) { // Crouching // if we are approaching a crouch area, crouch // if there are no crouch areas coming up, stand const float crouchRange = 50.0f; bool didCrouch = false; for (int i = prevIndex; i < m_pathLength; i++) { const CNavArea *to = m_path[i].area; // if there is a jump area on the way to the crouch area, don't crouch as it messes up the jump // unless we are already higher than the jump area - we must've jumped already but not moved into next area if ((to->GetAttributes() & NAV_JUMP)/* && to->GetCenter()->z > GetFeetZ()*/) break; Vector close; to->GetClosestPointOnArea(&pev->origin, &close); if ((close - pev->origin).Make2D().IsLengthGreaterThan(crouchRange)) break; if (to->GetAttributes() & NAV_CROUCH) { Crouch(); didCrouch = true; break; } } if (!didCrouch && !IsJumping()) { // no crouch areas coming up StandUp(); } // end crouching logic } // compute our forward facing angle m_forwardAngle = UTIL_VecToYaw(m_goalPosition - pev->origin); // Look farther down the path to "lead" our view around corners Vector toGoal; if (m_pathIndex == 0) { toGoal = m_path[1].pos; } else if (m_pathIndex < m_pathLength) { toGoal = m_path[m_pathIndex].pos - pev->origin; // actually aim our view farther down the path const float lookAheadRange = 500.0f; if (!m_path[m_pathIndex].ladder && !IsNearJump() && toGoal.Make2D().IsLengthLessThan(lookAheadRange)) { float along = toGoal.Length2D(); int i; for (i = m_pathIndex + 1; i < m_pathLength; i++) { Vector delta = m_path[i].pos - m_path[i - 1].pos; float segmentLength = delta.Length2D(); if (along + segmentLength >= lookAheadRange) { // interpolate between points to keep look ahead point at fixed distance float t = (lookAheadRange - along) / (segmentLength + along); Vector target; if (t <= 0.0f) target = m_path[i - 1].pos; else if (t >= 1.0f) target = m_path[i].pos; else target = m_path[i - 1].pos + t * delta; toGoal = target - pev->origin; break; } // if we are coming up to a ladder or a jump, look at it if (m_path[i].ladder || (m_path[i].area->GetAttributes() & NAV_JUMP)) { toGoal = m_path[i].pos - pev->origin; break; } along += segmentLength; } if (i == m_pathLength) { toGoal = GetPathEndpoint() - pev->origin; } } } else { toGoal = GetPathEndpoint() - pev->origin; } m_lookAheadAngle = UTIL_VecToYaw(toGoal); // initialize "adjusted" goal to current goal Vector adjustedGoal = m_goalPosition; // Use short "feelers" to veer away from close-range obstacles // Feelers come from our ankles, just above StepHeight, so we avoid short walls, too // Don't use feelers if very near the end of the path, or about to jump // TODO: Consider having feelers at several heights to deal with overhangs, etc. if (!nearEndOfPath && !IsNearJump() && !IsJumping()) { FeelerReflexAdjustment(&adjustedGoal); } // draw debug visualization if ((cv_bot_traceview.value == 1.0f && IsLocalPlayerWatchingMe()) || cv_bot_traceview.value == 10.0f) { DrawPath(); const Vector *pos = &m_path[m_pathIndex].pos; UTIL_DrawBeamPoints(*pos, *pos + Vector(0, 0, 50), 1, 255, 255, 0); UTIL_DrawBeamPoints(adjustedGoal, adjustedGoal + Vector(0, 0, 50), 1, 255, 0, 255); UTIL_DrawBeamPoints(pev->origin, adjustedGoal + Vector(0, 0, 50), 1, 255, 0, 255); } // dont use adjustedGoal, as it can vary wildly from the feeler adjustment if (!IsAttacking() && IsFriendInTheWay(&m_goalPosition)) { if (!m_isWaitingBehindFriend) { m_isWaitingBehindFriend = true; const float politeDuration = 5.0f - 3.0f * GetProfile()->GetAggression(); m_politeTimer.Start(politeDuration); } else if (m_politeTimer.IsElapsed()) { // we have run out of patience m_isWaitingBehindFriend = false; ResetStuckMonitor(); // repath to avoid clump of friends in the way DestroyPath(); } } else if (m_isWaitingBehindFriend) { // we're done waiting for our friend to move m_isWaitingBehindFriend = false; ResetStuckMonitor(); } // Move along our path if there are no friends blocking our way, // or we have run out of patience if (!m_isWaitingBehindFriend || m_politeTimer.IsElapsed()) { // Move along path MoveTowardsPosition(&adjustedGoal); // Stuck check if (m_isStuck && !IsJumping()) { Wiggle(); } } // if our goal is high above us, we must have fallen bool didFall = false; if (m_goalPosition.z - GetFeetZ() > JumpCrouchHeight) { const float closeRange = 75.0f; Vector2D to(pev->origin.x - m_goalPosition.x, pev->origin.y - m_goalPosition.y); if (to.IsLengthLessThan(closeRange)) { // we can't reach the goal position // check if we can reach the next node, in case this was a "jump down" situation if (m_pathIndex < m_pathLength - 1) { if (m_path[m_pathIndex + 1].pos.z - GetFeetZ() > JumpCrouchHeight) { // the next node is too high, too - we really did fall of the path didFall = true; } } else { // fell trying to get to the last node in the path didFall = true; } } } // This timeout check is needed if the bot somehow slips way off // of its path and cannot progress, but also moves around // enough that it never becomes "stuck" const float giveUpDuration = 5.0f; // 4.0f if (didFall || gpGlobals->time - m_areaEnteredTimestamp > giveUpDuration) { if (didFall) { PrintIfWatched("I fell off!\n"); } // if we havent made any progress in a long time, give up if (m_pathIndex < m_pathLength - 1) { PrintIfWatched("Giving up trying to get to area #%d\n", m_path[m_pathIndex].area->GetID()); } else { PrintIfWatched("Giving up trying to get to end of path\n"); } Run(); StandUp(); DestroyPath(); return PATH_FAILURE; } return PROGRESSING; }
// Do reflex avoidance movements if our "feelers" are touched void CCSBot::FeelerReflexAdjustment(Vector *goalPosition) { // if we are in a "precise" area, do not do feeler adjustments if (m_lastKnownArea && (m_lastKnownArea->GetAttributes() & NAV_PRECISE)) return; Vector dir(BotCOS(m_forwardAngle), BotSIN(m_forwardAngle), 0.0f); Vector lat(-dir.y, dir.x, 0.0f); const float feelerOffset = (IsCrouching()) ? 15.0f : 20.0f; const float feelerLengthRun = 50.0f; // 100 - too long for tight hallways (cs_747) const float feelerLengthWalk = 30.0f; const float feelerHeight = StepHeight + 0.1f; // if obstacle is lower than StepHeight, we'll walk right over it float feelerLength = (IsRunning()) ? feelerLengthRun : feelerLengthWalk; feelerLength = (IsCrouching()) ? 20.0f : feelerLength; // Feelers must follow floor slope float ground; Vector normal; //m_eyePos = EyePosition(); m_eyePos.x = pev->origin.x + pev->view_ofs.x; m_eyePos.y = pev->origin.y + pev->view_ofs.y; m_eyePos.z = pev->origin.z + pev->view_ofs.z; if (GetSimpleGroundHeightWithFloor(&m_eyePos, &ground, &normal) == false) return; // get forward vector along floor dir = CrossProduct(lat, normal); // correct the sideways vector lat = CrossProduct(dir, normal); Vector feet(pev->origin.x, pev->origin.y, GetFeetZ()); feet.z += feelerHeight; Vector from = feet + feelerOffset * lat; Vector to = from + feelerLength * dir; bool leftClear = IsWalkableTraceLineClear(from, to, WALK_THRU_EVERYTHING); // avoid ledges, too // use 'from' so it doesn't interfere with legitimate gap jumping (its at our feet) // TODO: Rethink this - it causes lots of wiggling when bots jump down from vents, etc /* float ground; if (GetSimpleGroundHeightWithFloor(&from, &ground)) { if (GetFeetZ() - ground > JumpHeight) leftClear = false; } */ if ((cv_bot_traceview.value == 1.0f && IsLocalPlayerWatchingMe()) || cv_bot_traceview.value == 10.0f) { if (leftClear) UTIL_DrawBeamPoints(from, to, 1, 0, 255, 0); else UTIL_DrawBeamPoints(from, to, 1, 255, 0, 0); } from = feet - feelerOffset * lat; to = from + feelerLength * dir; bool rightClear = IsWalkableTraceLineClear(from, to, WALK_THRU_EVERYTHING); /* // avoid ledges, too if (GetSimpleGroundHeightWithFloor(&from, &ground)) { if (GetFeetZ() - ground > JumpHeight) rightClear = false; } */ if ((cv_bot_traceview.value == 1.0f && IsLocalPlayerWatchingMe()) || cv_bot_traceview.value == 10.0f) { if (rightClear) UTIL_DrawBeamPoints(from, to, 1, 0, 255, 0); else UTIL_DrawBeamPoints(from, to, 1, 255, 0, 0); } const float avoidRange = (IsCrouching()) ? 150.0f : 300.0f; // 50.0f : 300.0f if (!rightClear) { if (leftClear) { // right hit, left clear - veer left *goalPosition = *goalPosition + avoidRange * lat; } } else if (!leftClear) { // right clear, left hit - veer right *goalPosition = *goalPosition - avoidRange * lat; } }