//========= Copyright Valve Corporation, All rights reserved. ============// #include "cbase.h" #include "tf_player.h" #include "tf_projectile_arrow.h" #include "tf_weapon_grenade_pipebomb.h" #include "archer_proxy.h" LINK_ENTITY_TO_CLASS( archer_proxy, CTFArcherProxy ); ConVar tf_archer_proxy_fire_rate( "tf_archer_proxy_fire_rate", "1", FCVAR_CHEAT ); //-------------------------------------------------------------------------------------- void CTFArcherProxy::Precache( void ) { // don't need to precache, since player does this for us BaseClass::Precache(); } //-------------------------------------------------------------------------------------- void CTFArcherProxy::Spawn( void ) { BaseClass::Spawn(); SetThink( &CTFArcherProxy::Update ); SetNextThink( gpGlobals->curtime + RandomFloat( 0.1, 1.0f ) * tf_archer_proxy_fire_rate.GetFloat() ); m_state = HIDDEN; m_timer.Invalidate(); AddEffects( EF_NODRAW ); m_homePos = GetAbsOrigin(); SetModel( "models/weapons/w_models/w_arrow.mdl" ); } //--------------------------------------------------------------------------------------------- CTFPlayer *CTFArcherProxy::SelectTarget( void ) { // collect everyone CUtlVector< CTFPlayer * > playerVector; CollectPlayers( &playerVector, TF_TEAM_RED, COLLECT_ONLY_LIVING_PLAYERS ); CollectPlayers( &playerVector, TF_TEAM_BLUE, COLLECT_ONLY_LIVING_PLAYERS, APPEND_PLAYERS ); CTFPlayer *newVictim = NULL; float victimRangeSq = FLT_MAX; trace_t result; for( int i=0; iGetAbsOrigin() - GetAbsOrigin() ).LengthSqr(); if ( rangeSq < victimRangeSq ) { UTIL_TraceLine( GetAbsOrigin(), playerVector[i]->EyePosition(), MASK_SOLID, this, COLLISION_GROUP_DEBRIS, &result ); if ( !result.DidHit() ) { newVictim = playerVector[i]; victimRangeSq = rangeSq; } } } return newVictim; } //-------------------------------------------------------------------------------------- void CTFArcherProxy::Update( void ) { SetNextThink( gpGlobals->curtime ); switch( m_state ) { case HIDDEN: m_state = EMERGE; RemoveEffects( EF_NODRAW ); m_timer.Start( 1.0f ); break; case EMERGE: m_state = AIM_AND_FIRE; m_timer.Start( 1.0f ); break; case AIM_AND_FIRE: { CTFPlayer *target = SelectTarget(); if ( target ) { Vector to = target->GetAbsOrigin() - GetAbsOrigin(); QAngle angles; VectorAngles( to, angles ); SetAbsAngles( angles ); if ( m_timer.IsElapsed() ) { ShootArrowAt( target ); // ShootGrenadeAt( target ); m_state = HIDE; m_timer.Start( 1.0f ); } } break; } case HIDE: if ( m_timer.IsElapsed() ) { m_state = HIDDEN; AddEffects( EF_NODRAW ); m_timer.Start( 1.0f ); } break; } } //-------------------------------------------------------------------------------------- void CTFArcherProxy::ShootArrowAt( CBaseEntity *target ) { if ( !target ) { return; } Vector to = target->EyePosition() - GetAbsOrigin(); to.NormalizeInPlace(); QAngle angles; VectorAngles( to, angles ); const float arrowSpeed = 2600.0f; const float arrowGravity = 0.1f; CTFProjectile_Arrow *arrow = CTFProjectile_Arrow::Create( GetAbsOrigin() + 20.0f * to, angles, arrowSpeed, arrowGravity, TF_PROJECTILE_ARROW, this, this ); if ( arrow ) { arrow->SetLauncher( this ); arrow->SetCritical( false ); arrow->SetDamage( 100.0f ); EmitSound( "Weapon_CompoundBow.Single" ); } } //-------------------------------------------------------------------------------------- void CTFArcherProxy::ShootGrenadeAt( CBaseEntity *target ) { if ( !target ) { return; } Vector to = target->EyePosition() - GetAbsOrigin(); to.NormalizeInPlace(); QAngle angles; VectorAngles( to, angles ); float launchSpeed = 1000.0f; Vector velocity = ( to * launchSpeed ) + ( Vector( 0, 0, 1.0f ) * 200.0f ); AngularImpulse angVelocity = AngularImpulse( 600, random->RandomInt( -1200, 1200 ), 0 ); CTFGrenadePipebombProjectile *grenade = static_cast< CTFGrenadePipebombProjectile * >( CBaseEntity::CreateNoSpawn( "tf_projectile_pipe", GetAbsOrigin(), angles, NULL ) ); if ( grenade ) { // Set the pipebomb mode before calling spawn, so the model & associated vphysics get setup properly grenade->SetPipebombMode(); DispatchSpawn( grenade ); const int damage = 100; const float radius = 100.0f; grenade->InitGrenade( velocity, angVelocity, NULL, damage, radius ); grenade->m_flFullDamage = grenade->GetDamage(); grenade->ApplyLocalAngularVelocityImpulse( angVelocity ); grenade->SetCritical( false ); grenade->SetLauncher( this ); } }