Ejemplo n.º 1
0
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();
}
Ejemplo n.º 2
0
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;
}
Ejemplo n.º 3
0
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();
}
Ejemplo n.º 4
0
/**
 * 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();
}
Ejemplo n.º 5
0
// 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;
}
Ejemplo n.º 6
0
// 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;
}