diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/server/cstrike/bot/states/cs_bot_follow.cpp | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'game/server/cstrike/bot/states/cs_bot_follow.cpp')
| -rw-r--r-- | game/server/cstrike/bot/states/cs_bot_follow.cpp | 366 |
1 files changed, 366 insertions, 0 deletions
diff --git a/game/server/cstrike/bot/states/cs_bot_follow.cpp b/game/server/cstrike/bot/states/cs_bot_follow.cpp new file mode 100644 index 0000000..c8d4976 --- /dev/null +++ b/game/server/cstrike/bot/states/cs_bot_follow.cpp @@ -0,0 +1,366 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +// Author: Michael S. Booth ([email protected]), 2003 + +#include "cbase.h" +#include "cs_bot.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//-------------------------------------------------------------------------------------------------------------- +/** + * Follow our leader + */ +void FollowState::OnEnter( CCSBot *me ) +{ + me->StandUp(); + me->Run(); + me->DestroyPath(); + + m_isStopped = false; + m_stoppedTimestamp = 0.0f; + + // to force immediate repath + m_lastLeaderPos.x = -99999999.9f; + m_lastLeaderPos.y = -99999999.9f; + m_lastLeaderPos.z = -99999999.9f; + + m_lastSawLeaderTime = 0; + + // set re-pathing frequency + m_repathInterval.Invalidate(); + + m_isSneaking = false; + + m_walkTime.Invalidate(); + m_isAtWalkSpeed = false; + + m_leaderMotionState = INVALID; + m_idleTimer.Start( RandomFloat( 2.0f, 5.0f ) ); +} + +//-------------------------------------------------------------------------------------------------------------- +/** + * Determine the leader's motion state by tracking his speed + */ +void FollowState::ComputeLeaderMotionState( float leaderSpeed ) +{ + // walk = 130, run = 250 + const float runWalkThreshold = 140.0f; + const float walkStopThreshold = 10.0f; // 120.0f; + LeaderMotionStateType prevState = m_leaderMotionState; + if (leaderSpeed > runWalkThreshold) + { + m_leaderMotionState = RUNNING; + m_isAtWalkSpeed = false; + } + else if (leaderSpeed > walkStopThreshold) + { + // track when began to walk + if (!m_isAtWalkSpeed) + { + m_walkTime.Start(); + m_isAtWalkSpeed = true; + } + + const float minWalkTime = 0.25f; + if (m_walkTime.GetElapsedTime() > minWalkTime) + { + m_leaderMotionState = WALKING; + } + } + else + { + m_leaderMotionState = STOPPED; + m_isAtWalkSpeed = false; + } + + // track time spent in this motion state + if (prevState != m_leaderMotionState) + { + m_leaderMotionStateTime.Start(); + m_waitTime = RandomFloat( 1.0f, 3.0f ); + } +} + +//-------------------------------------------------------------------------------------------------------------- +/** + * Functor to collect all areas in the forward direction of the given player within a radius + */ +class FollowTargetCollector +{ +public: + FollowTargetCollector( CBasePlayer *player ) + { + m_player = player; + + Vector playerVel = player->GetAbsVelocity(); + m_forward.x = playerVel.x; + m_forward.y = playerVel.y; + float speed = m_forward.NormalizeInPlace(); + + Vector playerOrigin = GetCentroid( player ); + + const float walkSpeed = 100.0f; + if (speed < walkSpeed) + { + m_cutoff.x = playerOrigin.x; + m_cutoff.y = playerOrigin.y; + m_forward.x = 0.0f; + m_forward.y = 0.0f; + } + else + { + const float k = 1.5f; // 2.0f; + float trimSpeed = MIN( speed, 200.0f ); + m_cutoff.x = playerOrigin.x + k * trimSpeed * m_forward.x; + m_cutoff.y = playerOrigin.y + k * trimSpeed * m_forward.y; + } + + m_targetAreaCount = 0; + } + + enum { MAX_TARGET_AREAS = 128 }; + + bool operator() ( CNavArea *area ) + { + if (m_targetAreaCount >= MAX_TARGET_AREAS) + return false; + + // only use two-way connections + if (!area->GetParent() || area->IsConnected( area->GetParent(), NUM_DIRECTIONS )) + { + if (m_forward.IsZero()) + { + m_targetArea[ m_targetAreaCount++ ] = area; + } + else + { + // collect areas in the direction of the player's forward motion + Vector2D to( area->GetCenter().x - m_cutoff.x, area->GetCenter().y - m_cutoff.y ); + to.NormalizeInPlace(); + + //if (DotProduct( to, m_forward ) > 0.7071f) + if ((to.x * m_forward.x + to.y * m_forward.y) > 0.7071f) + m_targetArea[ m_targetAreaCount++ ] = area; + } + } + + return (m_targetAreaCount < MAX_TARGET_AREAS); + } + + + CBasePlayer *m_player; + Vector2D m_forward; + Vector2D m_cutoff; + + CNavArea *m_targetArea[ MAX_TARGET_AREAS ]; + int m_targetAreaCount; +}; + +//-------------------------------------------------------------------------------------------------------------- +/** + * Follow our leader + * @todo Clean up this nasty mess + */ +void FollowState::OnUpdate( CCSBot *me ) +{ + // if we lost our leader, give up + if (m_leader == NULL || !m_leader->IsAlive()) + { + me->Idle(); + return; + } + + // if we are carrying the bomb and at a bombsite, plant + if (me->HasC4() && me->IsAtBombsite()) + { + // plant it + me->SetTask( CCSBot::PLANT_BOMB ); + me->PlantBomb(); + + // radio to the team + me->GetChatter()->PlantingTheBomb( me->GetPlace() ); + + return; + } + + // look around + me->UpdateLookAround(); + + // if we are moving, we are not idle + if (me->IsNotMoving() == false) + m_idleTimer.Start( RandomFloat( 2.0f, 5.0f ) ); + + // compute the leader's speed + Vector leaderVel = m_leader->GetAbsVelocity(); + float leaderSpeed = Vector2D( leaderVel.x, leaderVel.y ).Length(); + + // determine our leader's movement state + ComputeLeaderMotionState( leaderSpeed ); + + // track whether we can see the leader + bool isLeaderVisible; + Vector leaderOrigin = GetCentroid( m_leader ); + if (me->IsVisible( leaderOrigin )) + { + m_lastSawLeaderTime = gpGlobals->curtime; + isLeaderVisible = true; + } + else + { + isLeaderVisible = false; + } + + + // determine whether we should sneak or not + const float farAwayRange = 750.0f; + Vector myOrigin = GetCentroid( me ); + if ((leaderOrigin - myOrigin).IsLengthGreaterThan( farAwayRange )) + { + // far away from leader - run to catch up + m_isSneaking = false; + } + else if (isLeaderVisible) + { + // if we see leader walking and we are nearby, walk + if (m_leaderMotionState == WALKING) + m_isSneaking = true; + + // if we are sneaking and our leader starts running, stop sneaking + if (m_isSneaking && m_leaderMotionState == RUNNING) + m_isSneaking = false; + } + + // if we haven't seen the leader for a long time, run + const float longTime = 20.0f; + if (gpGlobals->curtime - m_lastSawLeaderTime > longTime) + m_isSneaking = false; + + if (m_isSneaking) + me->Walk(); + else + me->Run(); + + + bool repath = false; + + // if the leader has stopped, hide nearby + const float nearLeaderRange = 250.0f; + if (!me->HasPath() && m_leaderMotionState == STOPPED && m_leaderMotionStateTime.GetElapsedTime() > m_waitTime) + { + // throttle how often this check occurs + m_waitTime += RandomFloat( 1.0f, 3.0f ); + + // the leader has stopped - if we are close to him, take up a hiding spot + if ((leaderOrigin - myOrigin).IsLengthLessThan( nearLeaderRange )) + { + const float hideRange = 250.0f; + if (me->TryToHide( NULL, -1.0f, hideRange, false, USE_NEAREST )) + { + me->ResetStuckMonitor(); + return; + } + } + } + + // if we have been idle for awhile, move + if (m_idleTimer.IsElapsed()) + { + repath = true; + + // always walk when we move such a short distance + m_isSneaking = true; + } + + // if our leader has moved, repath (don't repath if leading is stopping) + if (leaderSpeed > 100.0f && m_leaderMotionState != STOPPED) + { + repath = true; + } + + // move along our path + if (me->UpdatePathMovement( NO_SPEED_CHANGE ) != CCSBot::PROGRESSING) + { + me->DestroyPath(); + } + + // recompute our path if necessary + if (repath && m_repathInterval.IsElapsed() && !me->IsOnLadder()) + { + // recompute our path to keep us near our leader + m_lastLeaderPos = leaderOrigin; + + me->ResetStuckMonitor(); + + const float runSpeed = 200.0f; + + const float collectRange = (leaderSpeed > runSpeed) ? 600.0f : 400.0f; // 400, 200 + FollowTargetCollector collector( m_leader ); + SearchSurroundingAreas( TheNavMesh->GetNearestNavArea( m_lastLeaderPos ), m_lastLeaderPos, collector, collectRange ); + + if (cv_bot_debug.GetBool()) + { + for( int i=0; i<collector.m_targetAreaCount; ++i ) + collector.m_targetArea[i]->Draw( /*255, 0, 0, 2*/ ); + } + + // move to one of the collected areas + if (collector.m_targetAreaCount) + { + CNavArea *target = NULL; + Vector targetPos; + + // if we are idle, pick a random area + if (m_idleTimer.IsElapsed()) + { + target = collector.m_targetArea[ RandomInt( 0, collector.m_targetAreaCount-1 ) ]; + targetPos = target->GetCenter(); + me->PrintIfWatched( "%4.1f: Bored. Repathing to a new nearby area\n", gpGlobals->curtime ); + } + else + { + me->PrintIfWatched( "%4.1f: Repathing to stay with leader.\n", gpGlobals->curtime ); + + // find closest area to where we are + CNavArea *area; + float closeRangeSq = 9999999999.9f; + Vector close; + + for( int a=0; a<collector.m_targetAreaCount; ++a ) + { + area = collector.m_targetArea[a]; + + area->GetClosestPointOnArea( myOrigin, &close ); + + float rangeSq = (myOrigin - close).LengthSqr(); + if (rangeSq < closeRangeSq) + { + target = area; + targetPos = close; + closeRangeSq = rangeSq; + } + } + } + + if (target == NULL || me->ComputePath( target->GetCenter(), FASTEST_ROUTE ) == false) + me->PrintIfWatched( "Pathfind to leader failed.\n" ); + + // throttle how often we repath + m_repathInterval.Start( 0.5f ); + + m_idleTimer.Reset(); + } + } +} + +//-------------------------------------------------------------------------------------------------------------- +void FollowState::OnExit( CCSBot *me ) +{ +} |