mirror of
https://github.com/nillerusr/source-engine.git
synced 2026-08-12 11:49:09 +00:00
1
This commit is contained in:
@@ -0,0 +1,336 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_escort_squad_leader.cpp
|
||||
// Escort the squad leader to their destination
|
||||
// Michael Booth, Octoboer 2011
|
||||
|
||||
#include "cbase.h"
|
||||
|
||||
#include "bot/tf_bot.h"
|
||||
#include "bot/behavior/squad/tf_bot_escort_squad_leader.h"
|
||||
#include "bot/behavior/scenario/capture_the_flag/tf_bot_deliver_flag.h"
|
||||
#include "bot/behavior/scenario/capture_the_flag/tf_bot_fetch_flag.h"
|
||||
|
||||
ConVar tf_bot_squad_escort_range( "tf_bot_squad_escort_range", "500", FCVAR_CHEAT );
|
||||
ConVar tf_bot_formation_debug( "tf_bot_formation_debug", "0", FCVAR_CHEAT );
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
CTFBotEscortSquadLeader::CTFBotEscortSquadLeader( Action< CTFBot > *actionToDoAfterSquadDisbands ) // : m_path( ChasePath::LEAD_SUBJECT )
|
||||
{
|
||||
m_actionToDoAfterSquadDisbands = actionToDoAfterSquadDisbands;
|
||||
m_formationPath.SetGoalTolerance( 0.0f );
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotEscortSquadLeader::OnStart( CTFBot *me, Action< CTFBot > *priorAction )
|
||||
{
|
||||
m_formationForward = vec3_origin;
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotEscortSquadLeader::Update( CTFBot *me, float interval )
|
||||
{
|
||||
if ( interval <= 0.0f )
|
||||
{
|
||||
return Continue();
|
||||
}
|
||||
|
||||
const CKnownEntity *threat = me->GetVisionInterface()->GetPrimaryKnownThreat();
|
||||
if ( threat && threat->IsVisibleRecently() )
|
||||
{
|
||||
// prepare to fight
|
||||
me->EquipBestWeaponForThreat( threat );
|
||||
}
|
||||
|
||||
CTFBotSquad *squad = me->GetSquad();
|
||||
if ( !squad )
|
||||
{
|
||||
if ( m_actionToDoAfterSquadDisbands )
|
||||
{
|
||||
return ChangeTo( m_actionToDoAfterSquadDisbands, "Not in a Squad" );
|
||||
}
|
||||
|
||||
return Done( "Not in a Squad" );
|
||||
}
|
||||
|
||||
// we need to update every tick to smoothly move in formation
|
||||
me->FlagForUpdate();
|
||||
|
||||
CTFBot *leader = squad->GetLeader();
|
||||
if ( !leader || !leader->IsAlive() )
|
||||
{
|
||||
me->LeaveSquad();
|
||||
|
||||
if ( m_actionToDoAfterSquadDisbands )
|
||||
{
|
||||
return ChangeTo( m_actionToDoAfterSquadDisbands, "Squad leader is dead" );
|
||||
}
|
||||
|
||||
return Done( "Squad leader is dead" );
|
||||
}
|
||||
|
||||
if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() && leader == me )
|
||||
{
|
||||
const char* pszNowLeader = "I'm now the squad leader! Going for the flag!";
|
||||
if ( me->HasAttribute( CTFBot::AGGRESSIVE ) )
|
||||
{
|
||||
// push for the point first, then attack
|
||||
return ChangeTo( new CTFBotPushToCapturePoint( new CTFBotFetchFlag ), pszNowLeader );
|
||||
}
|
||||
|
||||
// capture the flag
|
||||
return ChangeTo( new CTFBotFetchFlag, pszNowLeader );
|
||||
}
|
||||
|
||||
// if we're using a melee weapon, close and attack with it while staying near the leader
|
||||
CTFWeaponBase *myWeapon = me->m_Shared.GetActiveTFWeapon();
|
||||
if ( myWeapon && myWeapon->IsMeleeWeapon() )
|
||||
{
|
||||
if ( me->IsRangeLessThan( leader, tf_bot_squad_escort_range.GetFloat() ) && me->IsLineOfSightClear( leader ) )
|
||||
{
|
||||
ActionResult< CTFBot > result = m_meleeAttackAction.Update( me, interval );
|
||||
|
||||
if ( result.IsContinue() )
|
||||
{
|
||||
// we have a melee target, and we're still reasonably close to the flag leader
|
||||
return Continue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CUtlVector< CTFBot * > rawMemberVector;
|
||||
squad->CollectMembers( &rawMemberVector );
|
||||
|
||||
// cull out the medics - they do their own thing
|
||||
CUtlVector< CTFBot * > memberVector;
|
||||
for( int m=0; m<rawMemberVector.Count(); ++m )
|
||||
{
|
||||
if ( !rawMemberVector[m]->IsPlayerClass( TF_CLASS_MEDIC ) )
|
||||
{
|
||||
memberVector.AddToTail( rawMemberVector[m] );
|
||||
}
|
||||
}
|
||||
|
||||
const PathFollower *leaderPath = leader->GetCurrentPath();
|
||||
if ( !leaderPath || !leaderPath->GetCurrentGoal() )
|
||||
{
|
||||
// no path, no formation
|
||||
me->SetSquadFormationError( 0.0f );
|
||||
me->SetBrokenFormation( false );
|
||||
return Continue();
|
||||
}
|
||||
|
||||
const Path::Segment *leaderSegment = leaderPath->GetCurrentGoal();
|
||||
|
||||
Vector leaderForward = leaderSegment->pos - leader->GetAbsOrigin();
|
||||
|
||||
// if the leader is very close to the goal, use the next goal to ensure
|
||||
// the forward vector stays forward
|
||||
const float atGoal = 25.0f;
|
||||
if ( leaderForward.IsLengthLessThan( atGoal ) )
|
||||
{
|
||||
const Path::Segment *nextSegment = leaderPath->NextSegment( leaderSegment );
|
||||
if ( nextSegment )
|
||||
{
|
||||
leaderForward = nextSegment->pos - leader->GetAbsOrigin();
|
||||
}
|
||||
}
|
||||
|
||||
leaderForward.NormalizeInPlace();
|
||||
|
||||
if ( m_formationForward.IsZero() )
|
||||
{
|
||||
m_formationForward = leaderForward;
|
||||
}
|
||||
else
|
||||
{
|
||||
// limit rate of change of leader forward vector to keep formation coherent
|
||||
float maxRotation = 30.0f; // degrees/second
|
||||
|
||||
float leaderForwardYaw = UTIL_VecToYaw( leaderForward );
|
||||
float formationYaw = UTIL_VecToYaw( m_formationForward );
|
||||
|
||||
float angleDiff = UTIL_AngleDiff( leaderForwardYaw, formationYaw );
|
||||
|
||||
float deltaYaw = maxRotation * interval;
|
||||
|
||||
if ( angleDiff < -deltaYaw )
|
||||
{
|
||||
formationYaw -= deltaYaw;
|
||||
}
|
||||
else if ( angleDiff > deltaYaw )
|
||||
{
|
||||
formationYaw += deltaYaw;
|
||||
}
|
||||
else
|
||||
{
|
||||
formationYaw += angleDiff;
|
||||
}
|
||||
|
||||
FastSinCos( formationYaw * M_PI / 180.0f, &m_formationForward.y, &m_formationForward.x );
|
||||
m_formationForward.z = 0.0f;
|
||||
}
|
||||
|
||||
|
||||
const float maxSeparationAngle = 30.0f * M_PI / 180.0f;
|
||||
|
||||
float formationRadius = 125.0f;
|
||||
if ( squad->GetFormationSize() > 0.0f )
|
||||
{
|
||||
formationRadius = squad->GetFormationSize();
|
||||
}
|
||||
|
||||
Vector myFormationSpot;
|
||||
Vector formationForward = vec3_origin;
|
||||
float s, c;
|
||||
|
||||
// where am I in the roster
|
||||
int which;
|
||||
for( which=0; which<memberVector.Count(); ++which )
|
||||
{
|
||||
if ( me->IsSelf( memberVector[which] ) )
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// subtract one since the leader is always first
|
||||
--which;
|
||||
|
||||
// my formation spot is assigned via my position in the roster array
|
||||
int slot = ( which + 1 ) /2;
|
||||
|
||||
float formationAngle = slot * maxSeparationAngle;
|
||||
|
||||
if ( which & 0x1 )
|
||||
{
|
||||
formationAngle = -formationAngle;
|
||||
}
|
||||
|
||||
FastSinCos( formationAngle, &s, &c );
|
||||
formationForward.x = m_formationForward.x * c - m_formationForward.y * s;
|
||||
formationForward.y = m_formationForward.y * c + m_formationForward.x * s;
|
||||
|
||||
myFormationSpot = leader->GetAbsOrigin() + formationRadius * formationForward;
|
||||
|
||||
trace_t result;
|
||||
CTraceFilterIgnoreTeammates filter( me, COLLISION_GROUP_NONE, me->GetTeamNumber() );
|
||||
UTIL_TraceLine( leader->GetAbsOrigin() + Vector( 0, 0, HalfHumanHeight ), myFormationSpot + Vector( 0, 0, HalfHumanHeight ), MASK_PLAYERSOLID, &filter, &result );
|
||||
|
||||
if ( result.DidHitWorld() )
|
||||
{
|
||||
myFormationSpot = result.endpos - Vector( 0, 0, HalfHumanHeight ) + 0.6f * me->GetBodyInterface()->GetHullWidth() * result.plane.normal;
|
||||
}
|
||||
|
||||
|
||||
if ( tf_bot_formation_debug.GetBool() )
|
||||
{
|
||||
NDebugOverlay::Circle( myFormationSpot, 16.0f, 0, 255, 0, 255, true, 0.1f );
|
||||
|
||||
CFmtStr msg;
|
||||
NDebugOverlay::Text( myFormationSpot, msg.sprintf( "%d", which ), false, 0.1f );
|
||||
}
|
||||
|
||||
// match speed with leader if I'm at/near my formation position
|
||||
Vector to = myFormationSpot - me->GetAbsOrigin();
|
||||
float error = to.Length2D();
|
||||
const float maxError = 100.0f; // 50
|
||||
|
||||
float normalizedError = 1.0f;
|
||||
if ( error < maxError )
|
||||
{
|
||||
normalizedError = error / maxError;
|
||||
}
|
||||
|
||||
// this error term is used in CTFPlayer::TeamFortress_CalculateMaxSpeed() to
|
||||
// modulate our speed
|
||||
// 0 = in position (no error)
|
||||
// 1 = far out of position (max error)
|
||||
me->SetSquadFormationError( normalizedError );
|
||||
|
||||
// move to my formation spot
|
||||
if ( error < 50.0f )
|
||||
{
|
||||
// if we're ahead of where we want to be, just wait
|
||||
if ( DotProduct( to, formationForward ) > 0.0f )
|
||||
{
|
||||
// very close - just directly approach to avoid pathing jaggies
|
||||
me->GetLocomotionInterface()->Approach( myFormationSpot );
|
||||
}
|
||||
else
|
||||
{
|
||||
// we're in position
|
||||
me->SetSquadFormationError( 0.0f );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( m_pathTimer.IsElapsed() )
|
||||
{
|
||||
m_pathTimer.Start( RandomFloat( 0.1f, 0.2f ) );
|
||||
|
||||
me->SetBrokenFormation( false );
|
||||
|
||||
CTFBotPathCost cost( me, FASTEST_ROUTE );
|
||||
if ( m_formationPath.Compute( me, myFormationSpot, cost ) == false )
|
||||
{
|
||||
// no path back to formation
|
||||
me->SetBrokenFormation( true );
|
||||
}
|
||||
|
||||
// if we have a long path to get back in formation, we've broken ranks
|
||||
const float tooFar = 750.0f;
|
||||
if ( m_formationPath.GetLength() > tooFar )
|
||||
{
|
||||
me->SetBrokenFormation( true );
|
||||
}
|
||||
}
|
||||
|
||||
m_formationPath.Update( me );
|
||||
}
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
void CTFBotEscortSquadLeader::OnEnd( CTFBot *me, Action< CTFBot > *nextAction )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotWaitForOutOfPositionSquadMember::OnStart( CTFBot *me, Action< CTFBot > *priorAction )
|
||||
{
|
||||
m_waitTimer.Start( 2.0f );
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotWaitForOutOfPositionSquadMember::Update( CTFBot *me, float interval )
|
||||
{
|
||||
if ( m_waitTimer.IsElapsed() )
|
||||
{
|
||||
return Done( "Timeout" );
|
||||
}
|
||||
|
||||
if ( !me->IsInASquad() || !me->GetSquad()->IsLeader( me ) )
|
||||
{
|
||||
return Done( "No squad" );
|
||||
}
|
||||
|
||||
if ( me->GetSquad()->IsInFormation() )
|
||||
{
|
||||
// Everyone is in position
|
||||
return Done( "Everyone is in formation. Moving on." );
|
||||
}
|
||||
|
||||
return Continue();
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_escort_squad_leader.h
|
||||
// Escort the squad leader to their destination
|
||||
// Michael Booth, Octoboer 2011
|
||||
|
||||
#ifndef TF_BOT_ESCORT_SQUAD_LEADER_H
|
||||
#define TF_BOT_ESCORT_SQUAD_LEADER_H
|
||||
|
||||
|
||||
#include "Path/NextBotPathFollow.h"
|
||||
#include "bot/behavior/tf_bot_melee_attack.h"
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class CTFBotEscortSquadLeader : public Action< CTFBot >
|
||||
{
|
||||
public:
|
||||
CTFBotEscortSquadLeader( Action< CTFBot > *actionToDoAfterSquadDisbands = NULL );
|
||||
virtual ~CTFBotEscortSquadLeader() { }
|
||||
|
||||
virtual ActionResult< CTFBot > OnStart( CTFBot *me, Action< CTFBot > *priorAction );
|
||||
virtual ActionResult< CTFBot > Update( CTFBot *me, float interval );
|
||||
virtual void OnEnd( CTFBot *me, Action< CTFBot > *nextAction );
|
||||
|
||||
virtual const char *GetName( void ) const { return "EscortSquadLeader"; };
|
||||
|
||||
private:
|
||||
Action< CTFBot > *m_actionToDoAfterSquadDisbands;
|
||||
CTFBotMeleeAttack m_meleeAttackAction;
|
||||
|
||||
PathFollower m_formationPath;
|
||||
CountdownTimer m_pathTimer;
|
||||
|
||||
const Vector &GetFormationForwardVector( CTFBot *me );
|
||||
Vector m_formationForward;
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class CTFBotWaitForOutOfPositionSquadMember : public Action< CTFBot >
|
||||
{
|
||||
public:
|
||||
virtual ~CTFBotWaitForOutOfPositionSquadMember() { }
|
||||
|
||||
virtual ActionResult< CTFBot > OnStart( CTFBot *me, Action< CTFBot > *priorAction );
|
||||
virtual ActionResult< CTFBot > Update( CTFBot *me, float interval );
|
||||
|
||||
virtual const char *GetName( void ) const { return "WaitForOutOfPositionSquadMember"; };
|
||||
|
||||
private:
|
||||
CountdownTimer m_waitTimer;
|
||||
};
|
||||
|
||||
|
||||
#endif // TF_BOT_ESCORT_SQUAD_LEADER_H
|
||||
Reference in New Issue
Block a user