void CHostageImprov::__MAKE_VHOOK(OnReset)() { m_moveFlags = 0; m_moveType = Stopped; m_moveLimit = Running; m_vel.x = 0; m_vel.y = 0; m_actualVel = Vector(0, 0, 0); m_checkNearbyTerroristTimer.Invalidate(); m_lastKnownArea = NULL; m_hasKnownGoodPos = false; m_hasPriorKnownGoodPos = false; m_isTerroristNearby = false; m_isCrouching = false; SetMoveAngle(m_hostage->pev->angles.y); m_moveGoal = m_hostage->m_vStart; ClearLookAt(); ClearFaceTo(); m_blinkTimer.Invalidate(); m_blinkCounter = 0; m_hostage->SetBoneController(2, 0); m_hostage->SetBoneController(3, 0); m_hostage->SetBoneController(4, 0); m_path.Invalidate(); m_chatterTimer.Invalidate(); m_visiblePlayerCount = 0; m_isDelayedChatterPending = 0; m_animateState.Reset(); m_didFidget = 0; m_lastSawCT.Start(); m_lastSawT.Start(); m_scaredTimer.Invalidate(); m_jumpTimer.Invalidate(); m_hasJumped = false; m_isFriendInTheWay = false; m_aggression = RANDOM_FLOAT(0, 1); StandUp(); m_behavior.Reset(this); Idle(); }
bool CCSBot::DiscontinuityJump(float ground, bool onlyJumpDown, bool mustJump) { // don't try to jump again. if (m_isJumpCrouching) return false; float dz = ground - GetFeetZ(); if (dz > StepHeight && !onlyJumpDown) { // dont restrict jump time when going up if (Jump(MUST_JUMP)) { m_isJumpCrouching = true; m_isJumpCrouched = false; StandUp(); m_jumpCrouchTimestamp = gpGlobals->time; return true; } } else if (!IsUsingLadder() && dz < -JumpHeight) { if (Jump(mustJump)) { m_isJumpCrouching = true; m_isJumpCrouched = false; StandUp(); m_jumpCrouchTimestamp = gpGlobals->time; return true; } } return false; }
void CCSBot::ResetValues() { m_chatter.Reset(); m_gameState.Reset(); m_avoid = NULL; m_avoidTimestamp = 0.0f; m_hurryTimer.Invalidate(); m_isStuck = false; m_stuckTimestamp = 0.0f; m_wiggleTimestamp = 0.0f; m_stuckJumpTimestamp = 0.0f; m_pathLength = 0; m_pathIndex = 0; m_areaEnteredTimestamp = 0.0f; m_currentArea = NULL; m_lastKnownArea = NULL; m_avoidFriendTimer.Invalidate(); m_isFriendInTheWay = false; m_isWaitingBehindFriend = false; m_disposition = ENGAGE_AND_INVESTIGATE; m_enemy = NULL; m_isWaitingToTossGrenade = false; m_wasSafe = true; m_nearbyEnemyCount = 0; m_enemyPlace = 0; m_nearbyFriendCount = 0; m_closestVisibleFriend = NULL; m_closestVisibleHumanFriend = NULL; for (int w = 0; w < ARRAYSIZE(m_watchInfo); ++w) { m_watchInfo[w].timestamp = 0.0f; m_watchInfo[w].isEnemy = false; } m_isEnemyVisible = false; m_visibleEnemyParts = NONE; m_lastSawEnemyTimestamp = 0.0f; m_firstSawEnemyTimestamp = 0.0f; m_currentEnemyAcquireTimestamp = 0.0f; m_isLastEnemyDead = true; m_attacker = NULL; m_attackedTimestamp = 0.0f; m_enemyDeathTimestamp = 0.0f; m_lastVictimID = 0; m_isAimingAtEnemy = false; m_fireWeaponTimestamp = 0.0f; m_equipTimer.Invalidate(); m_isFollowing = false; m_leader = NULL; m_followTimestamp = 0.0f; m_allowAutoFollowTime = 0.0f; m_enemyQueueIndex = 0; m_enemyQueueCount = 0; m_enemyQueueAttendIndex = 0; m_bomber = NULL; m_lookAroundStateTimestamp = 0.0f; m_inhibitLookAroundTimestamp = 0.0f; m_lookPitch = 0.0f; m_lookPitchVel = 0.0f; m_lookYaw = 0.0f; m_lookYawVel = 0.0f; m_aimOffsetTimestamp = 0.0f; m_aimSpreadTimestamp = 0.0f; m_lookAtSpotState = NOT_LOOKING_AT_SPOT; m_spotEncounter = NULL; m_spotCheckTimestamp = 0.0f; m_peripheralTimestamp = 0.0f; m_avgVelIndex = 0; m_avgVelCount = 0; m_lastOrigin = (pev != NULL) ? pev->origin : Vector(0, 0, 0); m_lastRadioCommand = EVENT_INVALID; m_lastRadioRecievedTimestamp = 0.0f; m_lastRadioSentTimestamp = 0.0f; m_radioSubject = NULL; m_voiceFeedbackEndTimestamp = 0.0f; m_hostageEscortCount = 0; m_hostageEscortCountTimestamp = 0.0f; m_noisePosition = Vector(0, 0, 0); m_noiseTimestamp = 0.0f; m_noiseCheckTimestamp = 0.0f; m_isNoiseTravelRangeChecked = false; m_stateTimestamp = 0.0f; m_task = SEEK_AND_DESTROY; m_taskEntity = NULL; m_approachPointCount = 0; m_approachPointViewPosition = Vector(0, 0, 0); m_checkedHidingSpotCount = 0; m_isJumpCrouching = false; StandUp(); Run(); m_mustRunTimer.Invalidate(); m_repathTimer.Invalidate(); m_pathLadder = NULL; m_huntState.ClearHuntArea(); // adjust morale - if we died, our morale decreased, // but if we live, no adjustement (round win/loss also adjusts morale if (m_diedLastRound) DecreaseMorale(); m_diedLastRound = false; // IsRogue() randomly changes this m_isRogue = false; m_surpriseDelay = 0.0f; m_surpriseTimestamp = 0.0f; // even though these are EHANDLEs, they need to be NULL-ed m_goalEntity = NULL; m_avoid = NULL; m_enemy = NULL; for (int i = 0; i < MAX_ENEMY_QUEUE; ++i) { m_enemyQueue[i].player = NULL; m_enemyQueue[i].isReloading = false; m_enemyQueue[i].isProtectedByShield = false; } // start in idle state StopAttacking(); Idle(); }
/** * Reset internal data to initial state */ void CCFBot::ResetValues( void ) { m_chatter.Reset(); m_gameState.Reset(); m_avoid = NULL; m_avoidTimestamp = 0.0f; m_hurryTimer.Invalidate(); m_alertTimer.Invalidate(); m_sneakTimer.Invalidate(); m_noiseBendTimer.Invalidate(); m_bendNoisePositionValid = false; m_isStuck = false; m_stuckTimestamp = 0.0f; m_wiggleTimer.Invalidate(); m_stuckJumpTimer.Invalidate(); m_pathLength = 0; m_pathIndex = 0; m_areaEnteredTimestamp = 0.0f; m_currentArea = NULL; m_lastKnownArea = NULL; m_isStopping = false; m_avoidFriendTimer.Invalidate(); m_isFriendInTheWay = false; m_isWaitingBehindFriend = false; m_isAvoidingGrenade.Invalidate(); StopPanicking(); m_disposition = ENGAGE_AND_INVESTIGATE; m_enemy = NULL; m_grenadeTossState = NOT_THROWING; m_initialEncounterArea = NULL; m_wasSafe = true; m_nearbyEnemyCount = 0; m_enemyPlace = 0; m_nearbyFriendCount = 0; m_closestVisibleFriend = NULL; m_closestVisibleHumanFriend = NULL; m_closestVisibleKO = NULL; for( int w=0; w<MAX_PLAYERS; ++w ) { m_watchInfo[w].timestamp = 0.0f; m_watchInfo[w].isEnemy = false; m_playerTravelDistance[ w ] = -1.0f; } // randomly offset each bot's timer to spread computation out m_updateTravelDistanceTimer.Start( RandomFloat( 0.0f, 0.9f ) ); m_travelDistancePhase = 0; m_isEnemyVisible = false; m_visibleEnemyParts = VIS_NONE; m_lastSawEnemyTimestamp = -999.9f; m_firstSawEnemyTimestamp = 0.0f; m_currentEnemyAcquireTimestamp = 0.0f; m_isLastEnemyDead = true; m_attacker = NULL; m_attackedTimestamp = 0.0f; m_enemyDeathTimestamp = 0.0f; m_friendDeathTimestamp = 0.0f; m_lastVictimID = 0; m_isAimingAtEnemy = false; m_fireWeaponTimestamp = 0.0f; m_equipTimer.Invalidate(); m_zoomTimer.Invalidate(); m_isFollowing = false; m_leader = NULL; m_followTimestamp = 0.0f; m_allowAutoFollowTime = 0.0f; m_enemyQueueIndex = 0; m_enemyQueueCount = 0; m_enemyQueueAttendIndex = 0; m_bomber = NULL; m_isEnemySniperVisible = false; m_sawEnemySniperTimer.Invalidate(); m_lookAroundStateTimestamp = 0.0f; m_inhibitLookAroundTimestamp = 0.0f; m_lookPitch = 0.0f; m_lookPitchVel = 0.0f; m_lookYaw = 0.0f; m_lookYawVel = 0.0f; m_aimOffsetTimestamp = 0.0f; m_aimSpreadTimestamp = 0.0f; m_lookAtSpotState = NOT_LOOKING_AT_SPOT; for( int p=0; p<MAX_PLAYERS; ++p ) { m_partInfo[p].m_validFrame = 0; } m_spotEncounter = NULL; m_spotCheckTimestamp = 0.0f; m_peripheralTimestamp = 0.0f; m_avgVelIndex = 0; m_avgVelCount = 0; m_lastOrigin = GetCentroid(); m_voiceEndTimestamp = 0.0f; m_noisePosition = Vector( 0, 0, 0 ); m_noiseTimestamp = 0.0f; m_stateTimestamp = 0.0f; m_task = SEEK_AND_DESTROY; m_taskEntity = NULL; m_approachPointCount = 0; m_approachPointViewPosition.x = 99999999999.9f; m_approachPointViewPosition.y = 0.0f; m_approachPointViewPosition.z = 0.0f; m_checkedHidingSpotCount = 0; StandUp(); Run(); m_mustRunTimer.Invalidate(); m_waitTimer.Invalidate(); m_pathLadder = NULL; m_repathTimer.Invalidate(); m_huntState.ClearHuntArea(); m_hasVisitedEnemySpawn = false; m_stillTimer.Invalidate(); // adjust morale - if we died, our morale decreased, // but if we live, no adjustement (round win/loss also adjusts morale) if (m_diedLastRound) DecreaseMorale(); m_diedLastRound = false; // IsRogue() randomly changes this m_isRogue = false; m_surpriseTimer.Invalidate(); // even though these are EHANDLEs, they need to be NULL-ed m_goalEntity = NULL; m_avoid = NULL; m_enemy = NULL; for ( int i=0; i<MAX_ENEMY_QUEUE; ++i ) { m_enemyQueue[i].player = NULL; m_enemyQueue[i].isReloading = false; m_enemyQueue[i].isProtectedByShield = false; } // start in idle state m_isOpeningDoor = false; StopAttacking(); Idle(); }
// Navigate our current ladder. Return true if we are doing ladder navigation. // TODO: Need Push() and Pop() for run/walk context to keep ladder speed contained. bool CCSBot::UpdateLadderMovement() { if (!m_pathLadder) return false; bool giveUp = false; // check for timeout const float ladderTimeoutDuration = 10.0f; if (gpGlobals->time - m_pathLadderTimestamp > ladderTimeoutDuration) { PrintIfWatched("Ladder timeout!\n"); giveUp = true; } else if (m_pathLadderState == APPROACH_ASCENDING_LADDER || m_pathLadderState == APPROACH_DESCENDING_LADDER || m_pathLadderState == ASCEND_LADDER || m_pathLadderState == DESCEND_LADDER || m_pathLadderState == DISMOUNT_ASCENDING_LADDER || m_pathLadderState == MOVE_TO_DESTINATION) { if (m_isStuck) { PrintIfWatched("Giving up ladder - stuck\n"); giveUp = true; } } if (giveUp) { // jump off ladder and give up Jump(MUST_JUMP); Wiggle(); ResetStuckMonitor(); DestroyPath(); Run(); return false; } ResetStuckMonitor(); // check if somehow we totally missed the ladder switch (m_pathLadderState) { case MOUNT_ASCENDING_LADDER: case MOUNT_DESCENDING_LADDER: case ASCEND_LADDER: case DESCEND_LADDER: { const float farAway = 200.0f; Vector2D d = (m_pathLadder->m_top - pev->origin).Make2D(); if (d.IsLengthGreaterThan(farAway)) { PrintIfWatched("Missed ladder\n"); Jump(MUST_JUMP); DestroyPath(); Run(); return false; } break; } } m_areaEnteredTimestamp = gpGlobals->time; const float tolerance = 10.0f; const float closeToGoal = 25.0f; switch (m_pathLadderState) { case APPROACH_ASCENDING_LADDER: { bool approached = false; Vector2D d(pev->origin.x - m_goalPosition.x, pev->origin.y - m_goalPosition.y); if (d.x * m_pathLadder->m_dirVector.x + d.y * m_pathLadder->m_dirVector.y < 0.0f) { Vector2D perp(-m_pathLadder->m_dirVector.y, m_pathLadder->m_dirVector.x); #ifdef REGAMEDLL_FIXES if (Q_abs(d.x * perp.x + d.y * perp.y) < tolerance && d.Length() < closeToGoal) #else if (Q_abs(int64(d.x * perp.x + d.y * perp.y)) < tolerance && d.Length() < closeToGoal) #endif approached = true; } // small radius will just slow them down a little for more accuracy in hitting their spot const float walkRange = 50.0f; if (d.IsLengthLessThan(walkRange)) { Walk(); StandUp(); } // TODO: Check that we are on the ladder we think we are if (IsOnLadder()) { m_pathLadderState = ASCEND_LADDER; PrintIfWatched("ASCEND_LADDER\n"); // find actual top in case m_pathLadder penetrates the ceiling ComputeLadderEndpoint(true); } else if (approached) { // face the m_pathLadder m_pathLadderState = FACE_ASCENDING_LADDER; PrintIfWatched("FACE_ASCENDING_LADDER\n"); } else { // move toward ladder mount point MoveTowardsPosition(&m_goalPosition); } break; } case APPROACH_DESCENDING_LADDER: { // fall check if (GetFeetZ() <= m_pathLadder->m_bottom.z + HalfHumanHeight) { PrintIfWatched("Fell from ladder.\n"); m_pathLadderState = MOVE_TO_DESTINATION; m_path[m_pathIndex].area->GetClosestPointOnArea(&m_pathLadder->m_bottom, &m_goalPosition); AddDirectionVector(&m_goalPosition, m_pathLadder->m_dir, HalfHumanWidth); PrintIfWatched("MOVE_TO_DESTINATION\n"); } else { bool approached = false; Vector2D d(pev->origin.x - m_goalPosition.x, pev->origin.y - m_goalPosition.y); if (d.x * m_pathLadder->m_dirVector.x + d.y * m_pathLadder->m_dirVector.y > 0.0f) { Vector2D perp(-m_pathLadder->m_dirVector.y, m_pathLadder->m_dirVector.x); if (Q_abs(int64(d.x * perp.x + d.y * perp.y)) < tolerance && d.Length() < closeToGoal) approached = true; } // if approaching ladder from the side or "ahead", walk if (m_pathLadder->m_topBehindArea != m_lastKnownArea) { const float walkRange = 150.0f; if (!IsCrouching() && d.IsLengthLessThan(walkRange)) Walk(); } // TODO: Check that we are on the ladder we think we are if (IsOnLadder()) { // we slipped onto the ladder - climb it m_pathLadderState = DESCEND_LADDER; Run(); PrintIfWatched("DESCEND_LADDER\n"); // find actual bottom in case m_pathLadder penetrates the floor ComputeLadderEndpoint(false); } else if (approached) { // face the ladder m_pathLadderState = FACE_DESCENDING_LADDER; PrintIfWatched("FACE_DESCENDING_LADDER\n"); } else { // move toward ladder mount point MoveTowardsPosition(&m_goalPosition); } } break; } case FACE_ASCENDING_LADDER: { // find yaw to directly aim at ladder Vector to = m_pathLadder->m_bottom - pev->origin; Vector idealAngle = UTIL_VecToAngles(to); const float angleTolerance = 5.0f; if (AnglesAreEqual(pev->v_angle.y, idealAngle.y, angleTolerance)) { // move toward ladder until we become "on" it Run(); ResetStuckMonitor(); m_pathLadderState = MOUNT_ASCENDING_LADDER; PrintIfWatched("MOUNT_ASCENDING_LADDER\n"); } break; } case FACE_DESCENDING_LADDER: { // find yaw to directly aim at ladder Vector to = m_pathLadder->m_top - pev->origin; Vector idealAngle = UTIL_VecToAngles(to); const float angleTolerance = 5.0f; if (AnglesAreEqual(pev->v_angle.y, idealAngle.y, angleTolerance)) { // move toward ladder until we become "on" it m_pathLadderState = MOUNT_DESCENDING_LADDER; ResetStuckMonitor(); PrintIfWatched("MOUNT_DESCENDING_LADDER\n"); } break; } case MOUNT_ASCENDING_LADDER: { if (IsOnLadder()) { m_pathLadderState = ASCEND_LADDER; PrintIfWatched("ASCEND_LADDER\n"); // find actual top in case m_pathLadder penetrates the ceiling ComputeLadderEndpoint(true); } MoveForward(); break; } case MOUNT_DESCENDING_LADDER: { // fall check if (GetFeetZ() <= m_pathLadder->m_bottom.z + HalfHumanHeight) { PrintIfWatched("Fell from ladder.\n"); m_pathLadderState = MOVE_TO_DESTINATION; m_path[m_pathIndex].area->GetClosestPointOnArea(&m_pathLadder->m_bottom, &m_goalPosition); AddDirectionVector(&m_goalPosition, m_pathLadder->m_dir, HalfHumanWidth); PrintIfWatched("MOVE_TO_DESTINATION\n"); } else { if (IsOnLadder()) { m_pathLadderState = DESCEND_LADDER; PrintIfWatched("DESCEND_LADDER\n"); // find actual bottom in case m_pathLadder penetrates the floor ComputeLadderEndpoint(false); } // move toward ladder mount point MoveForward(); } break; } case ASCEND_LADDER: { // run, so we can make our dismount jump to the side, if necessary Run(); // if our destination area requires us to crouch, do it if (m_path[m_pathIndex].area->GetAttributes() & NAV_CROUCH) Crouch(); // did we reach the top? if (GetFeetZ() >= m_pathLadderEnd) { // we reached the top - dismount m_pathLadderState = DISMOUNT_ASCENDING_LADDER; PrintIfWatched("DISMOUNT_ASCENDING_LADDER\n"); if (m_path[m_pathIndex].area == m_pathLadder->m_topForwardArea) m_pathLadderDismountDir = FORWARD; else if (m_path[m_pathIndex].area == m_pathLadder->m_topLeftArea) m_pathLadderDismountDir = LEFT; else if (m_path[m_pathIndex].area == m_pathLadder->m_topRightArea) m_pathLadderDismountDir = RIGHT; m_pathLadderDismountTimestamp = gpGlobals->time; } else if (!IsOnLadder()) { // we fall off the ladder, repath DestroyPath(); return false; } // move up ladder MoveForward(); break; } case DESCEND_LADDER: { Run(); float destHeight = m_pathLadderEnd + HalfHumanHeight; if (!IsOnLadder() || GetFeetZ() <= destHeight) { // we reached the bottom, or we fell off - dismount m_pathLadderState = MOVE_TO_DESTINATION; m_path[m_pathIndex].area->GetClosestPointOnArea(&m_pathLadder->m_bottom, &m_goalPosition); AddDirectionVector(&m_goalPosition, m_pathLadder->m_dir, HalfHumanWidth); PrintIfWatched("MOVE_TO_DESTINATION\n"); } // Move down ladder MoveForward(); break; } case DISMOUNT_ASCENDING_LADDER: { if (gpGlobals->time - m_pathLadderDismountTimestamp >= 0.4f) { m_pathLadderState = MOVE_TO_DESTINATION; m_path[m_pathIndex].area->GetClosestPointOnArea(&pev->origin, &m_goalPosition); PrintIfWatched("MOVE_TO_DESTINATION\n"); } // We should already be facing the dismount point if (m_pathLadderFaceIn) { switch (m_pathLadderDismountDir) { case LEFT: StrafeLeft(); break; case RIGHT: StrafeRight(); break; case FORWARD: MoveForward(); break; } } else { switch (m_pathLadderDismountDir) { case LEFT: StrafeRight(); break; case RIGHT: StrafeLeft(); break; case FORWARD: MoveBackward(); break; } } break; } case MOVE_TO_DESTINATION: { if (m_path[m_pathIndex].area->Contains(&pev->origin)) { // successfully traversed ladder and reached destination area // exit ladder state machine PrintIfWatched("Ladder traversed.\n"); m_pathLadder = nullptr; // incrememnt path index to next step beyond this ladder SetPathIndex(m_pathIndex + 1); return false; } MoveTowardsPosition(&m_goalPosition); break; } } return true; }
// 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; }