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.
246 lines
6.4 KiB
246 lines
6.4 KiB
//========= 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 ); |
|
|
|
|