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/tf/bot_npc/bot_npc_archer.cpp | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'game/server/tf/bot_npc/bot_npc_archer.cpp')
| -rw-r--r-- | game/server/tf/bot_npc/bot_npc_archer.cpp | 402 |
1 files changed, 402 insertions, 0 deletions
diff --git a/game/server/tf/bot_npc/bot_npc_archer.cpp b/game/server/tf/bot_npc/bot_npc_archer.cpp new file mode 100644 index 0000000..592def4 --- /dev/null +++ b/game/server/tf/bot_npc/bot_npc_archer.cpp @@ -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 ); |