예제 #1
0
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);
}
예제 #2
0
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);
	}
}
예제 #3
0
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);
}
예제 #4
0
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);
}
예제 #5
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 );
}
예제 #6
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);
	}
}
예제 #7
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;
		}
	}
}
예제 #8
0
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;
	}
}
예제 #9
0
/**
 * 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();
	}

}
예제 #10
0
/**
 * 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 );
}
예제 #11
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;
}
예제 #12
0
// 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;
	}
}