You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
247 lines
6.4 KiB
247 lines
6.4 KiB
5 years ago
|
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||
|
// bot_npc_decoy.cpp
|
||
|
// A NextBot non-player decoy that imitates a real player
|
||
|
// Michael Booth, January 2011
|
||
|
|
||
|
#include "cbase.h"
|
||
|
#include "tf_player.h"
|
||
|
#include "tf_gamerules.h"
|
||
|
#include "tf_team.h"
|
||
|
#include "nav_mesh/tf_nav_area.h"
|
||
|
#include "bot_npc_decoy.h"
|
||
|
#include "econ_wearable.h"
|
||
|
|
||
|
LINK_ENTITY_TO_CLASS( bot_npc_decoy, CBotNPCDecoy );
|
||
|
PRECACHE_REGISTER( bot_npc_decoy );
|
||
|
|
||
|
ConVar tf_decoy_lifetime( "tf_decoy_lifetime", "5", FCVAR_CHEAT, "The lifetime of a decoy, in seconds" );
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------------------------------
|
||
|
CBotNPCDecoy::CBotNPCDecoy()
|
||
|
{
|
||
|
ALLOCATE_INTENTION_INTERFACE( CBotNPCDecoy );
|
||
|
|
||
|
m_locomotor = new CBotNPCDecoyLocomotion( this );
|
||
|
m_body = new CBotNPCBody( this );
|
||
|
|
||
|
m_eyeOffset = vec3_origin;
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------------------------------
|
||
|
CBotNPCDecoy::~CBotNPCDecoy()
|
||
|
{
|
||
|
DEALLOCATE_INTENTION_INTERFACE;
|
||
|
|
||
|
if ( m_locomotor )
|
||
|
delete m_locomotor;
|
||
|
|
||
|
if ( m_body )
|
||
|
delete m_body;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------------------------------
|
||
|
void CBotNPCDecoy::Precache()
|
||
|
{
|
||
|
BaseClass::Precache();
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------------------------------
|
||
|
void CBotNPCDecoy::Spawn( void )
|
||
|
{
|
||
|
BaseClass::Spawn();
|
||
|
|
||
|
SetCollisionGroup( COLLISION_GROUP_NONE );
|
||
|
SetSolid( SOLID_NONE );
|
||
|
AddSolidFlags( FSOLID_NOT_SOLID );
|
||
|
|
||
|
CTFPlayer *owner = ToTFPlayer( GetOwnerEntity() );
|
||
|
if ( !owner )
|
||
|
{
|
||
|
Warning( "Decoy spawned without an owner\n" );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
int ownerClass = owner->m_Shared.InCond( TF_COND_DISGUISED ) ? owner->m_Shared.GetDisguiseClass() : owner->GetPlayerClass()->GetClassIndex();
|
||
|
int ownerTeam = owner->m_Shared.InCond( TF_COND_DISGUISED ) ? owner->m_Shared.GetDisguiseTeam() : owner->GetTeamNumber();
|
||
|
|
||
|
SetModel( GetPlayerClassData( ownerClass )->m_szModelName );
|
||
|
ChangeTeam( ownerTeam );
|
||
|
|
||
|
if ( ownerTeam == TF_TEAM_BLUE )
|
||
|
{
|
||
|
m_nSkin = 1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
m_nSkin = 0;
|
||
|
}
|
||
|
|
||
|
SetAbsOrigin( owner->GetAbsOrigin() );
|
||
|
SetAbsAngles( owner->GetAbsAngles() );
|
||
|
SetAbsVelocity( owner->GetAbsVelocity() );
|
||
|
|
||
|
Vector headPos;
|
||
|
QAngle headAngles;
|
||
|
if ( GetAttachment( "head", headPos, headAngles ) )
|
||
|
{
|
||
|
m_eyeOffset = headPos - GetAbsOrigin();
|
||
|
}
|
||
|
|
||
|
CTFWeaponBase *theirWeapon = owner->m_Shared.GetDisguiseWeapon();
|
||
|
if ( !theirWeapon )
|
||
|
{
|
||
|
theirWeapon = owner->GetActiveTFWeapon();
|
||
|
}
|
||
|
|
||
|
if ( theirWeapon )
|
||
|
{
|
||
|
CBaseAnimating *weapon = (CBaseAnimating *)CreateEntityByName( "prop_dynamic" );
|
||
|
if ( weapon )
|
||
|
{
|
||
|
weapon->SetModel( theirWeapon->GetWorldModel() );
|
||
|
|
||
|
// bonemerge the weapon into our model
|
||
|
weapon->FollowEntity( this, true );
|
||
|
|
||
|
// choose the appropriate run animation for this weapon
|
||
|
switch( theirWeapon->GetTFWpnData().m_iWeaponType )
|
||
|
{
|
||
|
case TF_WPN_TYPE_PRIMARY:
|
||
|
m_runActivity = ACT_MP_RUN_PRIMARY;
|
||
|
break;
|
||
|
|
||
|
case TF_WPN_TYPE_SECONDARY:
|
||
|
m_runActivity = ACT_MP_RUN_SECONDARY;
|
||
|
break;
|
||
|
|
||
|
case TF_WPN_TYPE_MELEE:
|
||
|
default:
|
||
|
m_runActivity = ACT_MP_RUN_MELEE;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//---------------------------------------------------------------------------------------------
|
||
|
unsigned int CBotNPCDecoy::PhysicsSolidMaskForEntity( void ) const
|
||
|
{
|
||
|
// Only collide with the other team
|
||
|
int teamContents = ( GetTeamNumber() == TF_TEAM_RED ) ? CONTENTS_BLUETEAM : CONTENTS_REDTEAM;
|
||
|
|
||
|
return BaseClass::PhysicsSolidMaskForEntity() | teamContents;
|
||
|
}
|
||
|
|
||
|
|
||
|
//---------------------------------------------------------------------------------------------
|
||
|
bool CBotNPCDecoy::ShouldCollide( int collisionGroup, int contentsMask ) const
|
||
|
{
|
||
|
if ( collisionGroup == COLLISION_GROUP_PLAYER_MOVEMENT )
|
||
|
{
|
||
|
switch( GetTeamNumber() )
|
||
|
{
|
||
|
case TF_TEAM_RED:
|
||
|
if ( !( contentsMask & CONTENTS_REDTEAM ) )
|
||
|
return false;
|
||
|
break;
|
||
|
|
||
|
case TF_TEAM_BLUE:
|
||
|
if ( !( contentsMask & CONTENTS_BLUETEAM ) )
|
||
|
return false;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return BaseClass::ShouldCollide( collisionGroup, contentsMask );
|
||
|
}
|
||
|
|
||
|
|
||
|
//---------------------------------------------------------------------------------------------
|
||
|
//---------------------------------------------------------------------------------------------
|
||
|
float CBotNPCDecoyLocomotion::GetRunSpeed( void ) const
|
||
|
{
|
||
|
CTFPlayer *owner = ToTFPlayer( GetBot()->GetEntity()->GetOwnerEntity() );
|
||
|
if ( !owner )
|
||
|
{
|
||
|
return 0.0f;
|
||
|
}
|
||
|
|
||
|
int ownerClass = owner->m_Shared.InCond( TF_COND_DISGUISED ) ? owner->m_Shared.GetDisguiseClass() : owner->GetPlayerClass()->GetClassIndex();
|
||
|
return GetPlayerClassData( ownerClass )->m_flMaxSpeed;
|
||
|
}
|
||
|
|
||
|
|
||
|
//---------------------------------------------------------------------------------------------
|
||
|
// return maximum acceleration of locomotor
|
||
|
float CBotNPCDecoyLocomotion::GetMaxAcceleration( void ) const
|
||
|
{
|
||
|
return 1500.0f;
|
||
|
}
|
||
|
|
||
|
|
||
|
//---------------------------------------------------------------------------------------------
|
||
|
// return maximum deceleration of locomotor
|
||
|
float CBotNPCDecoyLocomotion::GetMaxDeceleration( void ) const
|
||
|
{
|
||
|
return 1500.0f;
|
||
|
}
|
||
|
|
||
|
|
||
|
//---------------------------------------------------------------------------------------------
|
||
|
//---------------------------------------------------------------------------------------------
|
||
|
class CBotNPCDecoyBehavior : public Action< CBotNPCDecoy >
|
||
|
{
|
||
|
public:
|
||
|
virtual ActionResult< CBotNPCDecoy > OnStart( CBotNPCDecoy *me, Action< CBotNPCDecoy > *priorAction )
|
||
|
{
|
||
|
m_timer.Start( tf_decoy_lifetime.GetFloat() );
|
||
|
|
||
|
// play running animation
|
||
|
if ( !me->GetBodyInterface()->IsActivity( me->GetRunActivity() ) )
|
||
|
{
|
||
|
me->GetBodyInterface()->StartActivity( me->GetRunActivity() );
|
||
|
}
|
||
|
|
||
|
return Continue();
|
||
|
}
|
||
|
|
||
|
virtual ActionResult< CBotNPCDecoy > Update( CBotNPCDecoy *me, float interval )
|
||
|
{
|
||
|
if ( m_timer.IsElapsed() )
|
||
|
{
|
||
|
// we're out of time
|
||
|
UTIL_Remove( me );
|
||
|
return Done( "Lifetime expired" );
|
||
|
}
|
||
|
|
||
|
CTFPlayer *owner = ToTFPlayer( me->GetOwnerEntity() );
|
||
|
if ( !owner )
|
||
|
{
|
||
|
UTIL_Remove( me );
|
||
|
return Done( "No owner!" );
|
||
|
}
|
||
|
|
||
|
Vector forward;
|
||
|
me->GetVectors( &forward, NULL, NULL );
|
||
|
|
||
|
me->GetLocomotionInterface()->SetDesiredSpeed( FLT_MAX ); // this is just a rate limiter
|
||
|
me->GetLocomotionInterface()->Run();
|
||
|
me->GetLocomotionInterface()->Approach( me->GetAbsOrigin() + 100.0f * forward );
|
||
|
|
||
|
return Continue();
|
||
|
}
|
||
|
|
||
|
virtual const char *GetName( void ) const { return "Behavior"; } // return name of this action
|
||
|
|
||
|
private:
|
||
|
CountdownTimer m_timer;
|
||
|
};
|
||
|
|
||
|
|
||
|
IMPLEMENT_INTENTION_INTERFACE( CBotNPCDecoy, CBotNPCDecoyBehavior );
|
||
|
|