mirror of
https://github.com/nillerusr/source-engine.git
synced 2026-08-11 03:09:36 +00:00
1
This commit is contained in:
@@ -0,0 +1,402 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// bot_npc_archer.cpp
|
||||
// A NextBot non-player derived archer
|
||||
// Michael Booth, November 2010
|
||||
|
||||
#include "cbase.h"
|
||||
|
||||
#include "tf_player.h"
|
||||
#include "tf_gamerules.h"
|
||||
#include "tf_team.h"
|
||||
#include "tf_projectile_arrow.h"
|
||||
#include "tf_weapon_grenade_pipebomb.h"
|
||||
#include "nav_mesh/tf_nav_area.h"
|
||||
#include "bot_npc_archer.h"
|
||||
#include "NextBot/Path/NextBotChasePath.h"
|
||||
#include "econ_wearable.h"
|
||||
#include "team_control_point_master.h"
|
||||
#include "particle_parse.h"
|
||||
#include "CRagdollMagnet.h"
|
||||
#include "NextBot/Behavior/BehaviorMoveTo.h"
|
||||
|
||||
ConVar tf_bot_npc_archer_health( "tf_bot_npc_archer_health", "100", FCVAR_CHEAT );
|
||||
|
||||
ConVar tf_bot_npc_archer_speed( "tf_bot_npc_archer_speed", "100", FCVAR_CHEAT );
|
||||
|
||||
ConVar tf_bot_npc_archer_shoot_interval( "tf_bot_npc_archer_shoot_interval", "2", FCVAR_CHEAT ); // 2
|
||||
ConVar tf_bot_npc_archer_arrow_damage( "tf_bot_npc_archer_arrow_damage", "75", FCVAR_CHEAT );
|
||||
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
// The Bot NPC
|
||||
//-----------------------------------------------------------------------------------------------------
|
||||
LINK_ENTITY_TO_CLASS( bot_npc_archer, CBotNPCArcher );
|
||||
|
||||
PRECACHE_REGISTER( bot_npc_archer );
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------
|
||||
CBotNPCArcher::CBotNPCArcher()
|
||||
{
|
||||
ALLOCATE_INTENTION_INTERFACE( CBotNPCArcher );
|
||||
|
||||
m_locomotor = new NextBotGroundLocomotion( this );
|
||||
m_body = new CBotNPCBody( this );
|
||||
|
||||
m_eyeOffset = vec3_origin;
|
||||
m_homePos = vec3_origin;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------
|
||||
CBotNPCArcher::~CBotNPCArcher()
|
||||
{
|
||||
DEALLOCATE_INTENTION_INTERFACE;
|
||||
|
||||
if ( m_locomotor )
|
||||
delete m_locomotor;
|
||||
|
||||
if ( m_body )
|
||||
delete m_body;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------
|
||||
void CBotNPCArcher::Precache()
|
||||
{
|
||||
BaseClass::Precache();
|
||||
|
||||
PrecacheModel( "models/player/sniper.mdl" );
|
||||
PrecacheModel( "models/weapons/c_models/c_bow/c_bow.mdl" );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------
|
||||
void CBotNPCArcher::Spawn( void )
|
||||
{
|
||||
BaseClass::Spawn();
|
||||
|
||||
SetModel( "models/player/sniper.mdl" );
|
||||
|
||||
m_bow = (CBaseAnimating *)CreateEntityByName( "prop_dynamic" );
|
||||
if ( m_bow )
|
||||
{
|
||||
m_bow->SetModel( "models/weapons/c_models/c_bow/c_bow.mdl" );
|
||||
|
||||
// bonemerge into our model
|
||||
m_bow->FollowEntity( this, true );
|
||||
}
|
||||
|
||||
int health = tf_bot_npc_archer_health.GetInt();
|
||||
SetHealth( health );
|
||||
SetMaxHealth( health );
|
||||
|
||||
ChangeTeam( TF_TEAM_RED );
|
||||
|
||||
Vector headPos;
|
||||
QAngle headAngles;
|
||||
if ( GetAttachment( "head", headPos, headAngles ) )
|
||||
{
|
||||
m_eyeOffset = headPos - GetAbsOrigin();
|
||||
}
|
||||
|
||||
m_homePos = GetAbsOrigin();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
unsigned int CBotNPCArcher::PhysicsSolidMaskForEntity( void ) const
|
||||
{
|
||||
// Only collide with the other team
|
||||
int teamContents = ( GetTeamNumber() == TF_TEAM_RED ) ? CONTENTS_BLUETEAM : CONTENTS_REDTEAM;
|
||||
|
||||
return BaseClass::PhysicsSolidMaskForEntity() | teamContents;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
bool CBotNPCArcher::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 );
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
//---------------------------------------------------------------------------------------------
|
||||
class CBotNPCArcherSurrender : public Action< CBotNPCArcher >
|
||||
{
|
||||
public:
|
||||
virtual ActionResult< CBotNPCArcher > OnStart( CBotNPCArcher *me, Action< CBotNPCArcher > *priorAction );
|
||||
virtual const char *GetName( void ) const { return "Surrender"; } // return name of this action
|
||||
};
|
||||
|
||||
|
||||
inline ActionResult< CBotNPCArcher > CBotNPCArcherSurrender::OnStart( CBotNPCArcher *me, Action< CBotNPCArcher > *priorAction )
|
||||
{
|
||||
CBaseAnimating *bow = me->GetBow();
|
||||
if ( bow )
|
||||
{
|
||||
bow->AddEffects( EF_NODRAW );
|
||||
}
|
||||
|
||||
me->GetBodyInterface()->StartActivity( ACT_MP_STAND_LOSERSTATE );
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
//---------------------------------------------------------------------------------------------
|
||||
class CBotNPCArcherShootBow : public Action< CBotNPCArcher >
|
||||
{
|
||||
public:
|
||||
CBotNPCArcherShootBow( CTFPlayer *target )
|
||||
{
|
||||
m_target = target;
|
||||
}
|
||||
|
||||
virtual ActionResult< CBotNPCArcher > OnStart( CBotNPCArcher *me, Action< CBotNPCArcher > *priorAction );
|
||||
virtual ActionResult< CBotNPCArcher > Update( CBotNPCArcher *me, float interval );
|
||||
|
||||
virtual const char *GetName( void ) const { return "ShootBow"; } // return name of this action
|
||||
|
||||
private:
|
||||
CHandle< CTFPlayer > m_target;
|
||||
};
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CBotNPCArcher > CBotNPCArcherShootBow::OnStart( CBotNPCArcher *me, Action< CBotNPCArcher > *priorAction )
|
||||
{
|
||||
if ( !m_target )
|
||||
{
|
||||
return Done( "No target" );
|
||||
}
|
||||
|
||||
me->GetLocomotionInterface()->FaceTowards( m_target->WorldSpaceCenter() );
|
||||
me->AddGesture( ACT_MP_ATTACK_STAND_ITEM2 );
|
||||
|
||||
// fire arrow
|
||||
const float arrowSpeed = 2000.0f;
|
||||
const float arrowGravity = 0.2f;
|
||||
|
||||
Vector muzzleOrigin;
|
||||
QAngle muzzleAngles;
|
||||
if ( me->GetBow()->GetAttachment( "muzzle", muzzleOrigin, muzzleAngles ) == false )
|
||||
{
|
||||
return Done( "No muzzle attachment!" );
|
||||
}
|
||||
|
||||
// lead target
|
||||
float range = me->GetRangeTo( m_target->EyePosition() );
|
||||
float flightTime = range / arrowSpeed;
|
||||
|
||||
Vector aimSpot = m_target->EyePosition() + m_target->GetAbsVelocity() * flightTime;
|
||||
|
||||
Vector to = aimSpot - muzzleOrigin;
|
||||
VectorAngles( to, muzzleAngles );
|
||||
|
||||
CTFProjectile_Arrow *arrow = CTFProjectile_Arrow::Create( muzzleOrigin, muzzleAngles, arrowSpeed, arrowGravity, TF_PROJECTILE_ARROW, me, me );
|
||||
if ( arrow )
|
||||
{
|
||||
arrow->SetLauncher( me );
|
||||
arrow->SetCritical( false );
|
||||
|
||||
arrow->SetDamage( tf_bot_npc_archer_arrow_damage.GetFloat() );
|
||||
|
||||
me->EmitSound( "Weapon_CompoundBow.Single" );
|
||||
}
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CBotNPCArcher > CBotNPCArcherShootBow::Update( CBotNPCArcher *me, float interval )
|
||||
{
|
||||
if ( me->IsSequenceFinished() )
|
||||
{
|
||||
return Done();
|
||||
}
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
//---------------------------------------------------------------------------------------------
|
||||
class CBotNPCArcherGuardSpot : public Action< CBotNPCArcher >
|
||||
{
|
||||
public:
|
||||
virtual ActionResult< CBotNPCArcher > OnStart( CBotNPCArcher *me, Action< CBotNPCArcher > *priorAction )
|
||||
{
|
||||
me->GetBodyInterface()->StartActivity( ACT_MP_STAND_ITEM2 );
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
CTFPlayer *GetVictim( CBotNPCArcher *me )
|
||||
{
|
||||
CUtlVector< CTFPlayer * > playerVector;
|
||||
CollectPlayers( &playerVector, TF_TEAM_BLUE, COLLECT_ONLY_LIVING_PLAYERS );
|
||||
|
||||
CTFPlayer *closeVictim = NULL;
|
||||
float victimRangeSq = FLT_MAX;
|
||||
|
||||
for( int i=0; i<playerVector.Count(); ++i )
|
||||
{
|
||||
float rangeSq = me->GetRangeSquaredTo( playerVector[i] );
|
||||
if ( rangeSq < victimRangeSq )
|
||||
{
|
||||
if ( playerVector[i]->m_Shared.IsStealthed() )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( me->IsLineOfSightClear( playerVector[i] ) )
|
||||
{
|
||||
closeVictim = playerVector[i];
|
||||
victimRangeSq = rangeSq;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return closeVictim;
|
||||
}
|
||||
|
||||
virtual ActionResult< CBotNPCArcher > Update( CBotNPCArcher *me, float interval )
|
||||
{
|
||||
if ( TFGameRules()->GetActiveBoss() == NULL )
|
||||
{
|
||||
// the Boss has been defeated - give up
|
||||
return ChangeTo( new CBotNPCArcherSurrender, "The Boss is dead! I give up!" );
|
||||
}
|
||||
|
||||
CTFPlayer *victim = GetVictim( me );
|
||||
|
||||
if ( victim )
|
||||
{
|
||||
// look at visible victim out of range
|
||||
me->GetLocomotionInterface()->FaceTowards( victim->WorldSpaceCenter() );
|
||||
|
||||
if ( m_shootTimer.IsElapsed() )
|
||||
{
|
||||
m_shootTimer.Start( tf_bot_npc_archer_shoot_interval.GetFloat() );
|
||||
|
||||
return SuspendFor( new CBotNPCArcherShootBow( victim ), "Fire!" );
|
||||
}
|
||||
}
|
||||
|
||||
if ( me->GetLocomotionInterface()->IsAttemptingToMove() )
|
||||
{
|
||||
// play running animation
|
||||
if ( !me->GetBodyInterface()->IsActivity( ACT_MP_DEPLOYED_IDLE_ITEM2 ) )
|
||||
{
|
||||
me->GetBodyInterface()->StartActivity( ACT_MP_DEPLOYED_IDLE_ITEM2 );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// standing still
|
||||
if ( !me->GetBodyInterface()->IsActivity( ACT_MP_STAND_ITEM2 ) )
|
||||
{
|
||||
me->GetBodyInterface()->StartActivity( ACT_MP_STAND_ITEM2 );
|
||||
}
|
||||
}
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
virtual const char *GetName( void ) const { return "GuardSpot"; } // return name of this action
|
||||
|
||||
private:
|
||||
CountdownTimer m_shootTimer;
|
||||
};
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
//---------------------------------------------------------------------------------------------
|
||||
class CBotNPCArcherMoveToMark : public Action< CBotNPCArcher >
|
||||
{
|
||||
public:
|
||||
virtual ActionResult< CBotNPCArcher > OnStart( CBotNPCArcher *me, Action< CBotNPCArcher > *priorAction )
|
||||
{
|
||||
ShortestPathCost cost;
|
||||
m_path.Compute( me, me->GetHomePosition(), cost );
|
||||
|
||||
me->GetBodyInterface()->StartActivity( ACT_MP_RUN_ITEM2 );
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
virtual ActionResult< CBotNPCArcher > Update( CBotNPCArcher *me, float interval )
|
||||
{
|
||||
m_path.Update( me );
|
||||
|
||||
if ( !m_path.IsValid() )
|
||||
{
|
||||
return ChangeTo( new CBotNPCArcherGuardSpot, "Reached my mark" );
|
||||
}
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
virtual const char *GetName( void ) const { return "MoveToMark"; } // return name of this action
|
||||
|
||||
private:
|
||||
PathFollower m_path;
|
||||
};
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
//---------------------------------------------------------------------------------------------
|
||||
class CBotNPCArcherBehavior : public Action< CBotNPCArcher >
|
||||
{
|
||||
public:
|
||||
virtual Action< CBotNPCArcher > *InitialContainedAction( CBotNPCArcher *me )
|
||||
{
|
||||
return new CBotNPCArcherMoveToMark;
|
||||
}
|
||||
|
||||
virtual ActionResult< CBotNPCArcher > Update( CBotNPCArcher *me, float interval )
|
||||
{
|
||||
return Continue();
|
||||
}
|
||||
|
||||
virtual EventDesiredResult< CBotNPCArcher > OnKilled( CBotNPCArcher *me, const CTakeDamageInfo &info )
|
||||
{
|
||||
// Calculate death force
|
||||
Vector forceVector = me->CalcDamageForceVector( info );
|
||||
|
||||
// See if there's a ragdoll magnet that should influence our force.
|
||||
CRagdollMagnet *magnet = CRagdollMagnet::FindBestMagnet( me );
|
||||
if ( magnet )
|
||||
{
|
||||
forceVector += magnet->GetForceVector( me );
|
||||
}
|
||||
|
||||
me->BecomeRagdoll( info, forceVector );
|
||||
|
||||
return TryDone();
|
||||
}
|
||||
|
||||
virtual const char *GetName( void ) const { return "Behavior"; } // return name of this action
|
||||
};
|
||||
|
||||
|
||||
IMPLEMENT_INTENTION_INTERFACE( CBotNPCArcher, CBotNPCArcherBehavior );
|
||||
Reference in New Issue
Block a user