예제 #1
0
//-----------------------------------------------------------------------------
// Purpose: Tell the driver to start firing at targets
//-----------------------------------------------------------------------------
void CNPC_VehicleDriver::InputStartFiring( inputdata_t &inputdata )
{
	CLEARBITS( m_spawnflags, SF_VEHICLEDRIVER_INACTIVE );
	SetCondition( COND_PROVOKED );

	float flMinRange, flMaxRange;
	// If the vehicle has a weapon, set our capability
	if ( m_pVehicleInterface->NPC_HasPrimaryWeapon() )
	{
		CapabilitiesAdd( bits_CAP_INNATE_RANGE_ATTACK1 );
		m_pVehicleInterface->Weapon_PrimaryRanges( &flMinRange, &flMaxRange );

		// Ensure the look distances is long enough
		if ( m_flDistTooFar < flMaxRange || GetSenses()->GetDistLook() < flMaxRange )
		{
			m_flDistTooFar = flMaxRange;
			SetDistLook( flMaxRange );
		}
	}

	if ( m_pVehicleInterface->NPC_HasSecondaryWeapon() )
	{
		CapabilitiesAdd( bits_CAP_INNATE_RANGE_ATTACK2 );
		m_pVehicleInterface->Weapon_SecondaryRanges( &flMinRange, &flMaxRange );

		// Ensure the look distances is long enough
		if ( m_flDistTooFar < flMaxRange || GetSenses()->GetDistLook() < flMaxRange )
		{
			m_flDistTooFar = flMaxRange;
			SetDistLook( flMaxRange );
		}
	}
}
//-----------------------------------------------------------------------------
// Purpose: The turret doesn't run base AI properly, which is a bad decision.
//			As a result, it has to manually find enemies.
//-----------------------------------------------------------------------------
void CNPC_Portal_FloorTurret::HackFindEnemy( void )
{
	// We have to refresh our memories before finding enemies, so
	// dead enemies are cleared out before new ones are added.
	GetEnemies()->RefreshMemories();

	GetSenses()->Look( PORTAL_FLOOR_TURRET_RANGE );
	SetEnemy( BestEnemy() );

	if ( GetEnemy() == NULL )
	{
		// Look through the list of sensed objects for possible targets
		AISightIter_t iter;
		CBaseEntity *pObject;
		CBaseEntity	*pNearest = NULL;
		float flClosestDistSqr = PORTAL_FLOOR_TURRET_RANGE * PORTAL_FLOOR_TURRET_RANGE;

		for ( pObject = GetSenses()->GetFirstSeenEntity( &iter, SEEN_MISC ); pObject; pObject = GetSenses()->GetNextSeenEntity( &iter ) )
		{
			Vector vVelocity;
			pObject->GetVelocity( &vVelocity );

			// Ignore objects going too slowly
			if ( vVelocity.LengthSqr() < m_fMovingTargetThreashold )
				continue;

			float flDistSqr = pObject->WorldSpaceCenter().DistToSqr( GetAbsOrigin() );
			if ( flDistSqr < flClosestDistSqr )
			{
				flClosestDistSqr = flDistSqr;
				pNearest = pObject;
			}
		}

		if ( pNearest )
		{
			SetEnemy( pNearest );
			m_fMovingTargetThreashold += gpGlobals->curtime * 15.0f;
			if ( m_fMovingTargetThreashold > 800.0f )
			{
				m_fMovingTargetThreashold = 800.0f;
			}
		}
	}
	else
	{
		m_fMovingTargetThreashold = 20.0f;
	}
}
//------------------------------------------------------------------------------
// Purpose :
// Input   :
// Output  :
//------------------------------------------------------------------------------
void CNPC_CombineDropship::Hunt( void )
{
	// If we have a pickup target, fly to it
	if ( m_hPickupTarget )
	{
		UpdatePickupNavigation();
	}
	else if ( m_iLandState == LANDING_NO )
	{
		UpdateTrackNavigation();
	}

	// look for enemy
	GetSenses()->Look( 4092 );
	ChooseEnemy();

	// don't face player ever, only face nav points
	Vector desiredDir = GetDesiredPosition() - GetAbsOrigin();
	VectorNormalize( desiredDir ); 
	// Face our desired position.
	m_vecDesiredFaceDir = desiredDir;

	Flight();

	UpdatePlayerDopplerShift( );

}
예제 #4
0
//-----------------------------------------------------------------------------
// Purpose: Watch for a target to wander into our view
//-----------------------------------------------------------------------------
void CNPC_CeilingTurret::AutoSearchThink( void )
{
	//Allow descended classes a chance to do something before the think function
	if ( PreThink( TURRET_AUTO_SEARCHING ) )
		return;

	//Spread out our thinking
	SetNextThink( gpGlobals->curtime + random->RandomFloat( 0.2f, 0.4f ) );

	//If the enemy is dead, find a new one
	if ( ( GetEnemy() != NULL ) && ( GetEnemy()->IsAlive() == false ) )
	{
		SetEnemy( NULL );
	}

	//Acquire Target
	if ( GetEnemy() == NULL )
	{
		GetSenses()->Look( CEILING_TURRET_RANGE );
		SetEnemy( BestEnemy() );
	}

	//Deploy if we've got an active target
	if ( GetEnemy() != NULL )
	{
		SetThink( &CNPC_CeilingTurret::Deploy );
		EmitSound( "NPC_CeilingTurret.Alert" );
	}
}
//------------------------------------------------------------------------------
// Purpose :
//------------------------------------------------------------------------------
void CNPC_Launcher::LauncherThink( void )
{
	if (gpGlobals->curtime > m_flNextAttack)
	{
		// If enemy was set, fire at enemy
		if (GetEnemy())
		{
			LaunchGrenade(GetEnemy());
			m_OnLaunch.FireOutput(GetEnemy(), this);
			m_flNextAttack = gpGlobals->curtime + m_nLaunchDelay;
		}
		// Otherwise look for enemy to fire at
		else
		{
			GetSenses()->Look((int)m_flMaxAttackDist);
			CBaseEntity* pBestEnemy = BestEnemy();

			if (pBestEnemy)
			{
				LaunchGrenade(pBestEnemy);
				m_OnLaunch.FireOutput(pBestEnemy, this);
				m_flNextAttack = gpGlobals->curtime + m_nLaunchDelay;
			}
		}
	}
	SetNextThink( gpGlobals->curtime + 0.1f );
}
예제 #6
0
//-----------------------------------------------------------------------------
// Purpose: Called when we have no scripted target. Looks for new enemies to track.
//-----------------------------------------------------------------------------
CBaseEntity *CNPC_CombineCamera::MaintainEnemy()
{
	if (HasSpawnFlags(SF_COMBINE_CAMERA_IGNOREENEMIES))
		return NULL;

	GetSenses()->Look(m_nOuterRadius);

	CBaseEntity *pEnemy = BestEnemy();
	if (pEnemy)
	{
		// See if our best enemy is too far away to care about.
		Vector vecDelta = pEnemy->GetAbsOrigin() - GetAbsOrigin();
		float flDist = vecDelta.Length();
		if (flDist < m_nOuterRadius)
		{
			if (FInViewCone(pEnemy))
			{
				// dvs: HACK: for checking multiple view cones
				float flSaveFieldOfView = m_flFieldOfView;
				m_flFieldOfView = CAMERA_FOV_NARROW;

				// Is the target visible?
				bool bVisible = FVisible(pEnemy);
				m_flFieldOfView = flSaveFieldOfView;
				if ( bVisible )
					return pEnemy;
			}
		}
	}
	
	return NULL;
}
//------------------------------------------------------------------------------
// Purpose :
// Input   :
// Output  :
//------------------------------------------------------------------------------
void CNPC_MissileDefense::RunAI( void )
{
	// If my enemy is dead clear the memory and reset m_hEnemy
	if (GetEnemy() != NULL && 
		!GetEnemy()->IsAlive())
	{
		ClearEnemyMemory();
		SetEnemy( NULL );
	}

	if (GetEnemy() == NULL )
	{
		GetSenses()->Look( 4092 );
		SetEnemy( BestEnemy( ) );

		if (GetEnemy() != NULL)
		{
			m_iAmmoLoaded = MD_FULLAMMO;
			m_flReloadedTime = gpGlobals->curtime;
		}
	}

	if( m_iAmmoLoaded < 1 && gpGlobals->curtime > m_flReloadedTime )
	{
		m_iAmmoLoaded = MD_FULLAMMO;
	}

	AimGun();
	FireCannons();
	SetNextThink( gpGlobals->curtime + 0.05 );
}
예제 #8
0
// 
// This think function will deploy the turret when something comes into range. This is for
// automatically activated turrets.
//
void CBaseTurret::AutoSearchThink(void)
{
	// ensure rethink
	StudioFrameAdvance( );
	SetNextThink( gpGlobals->curtime + random->RandomFloat( 0.2, 0.3 ) );

	// If we have a target and we're still healthy

	if (GetEnemy() != NULL)
	{
		if (!GetEnemy()->IsAlive() )
			SetEnemy( NULL );// Dead enemy forces a search for new one
	}

	// Acquire Target

	if (GetEnemy() == NULL)
	{
		GetSenses()->Look( TURRET_RANGE );
		SetEnemy( BestEnemy() );
	}

	if (GetEnemy() != NULL)
	{
		SetThink(Deploy);
		EmitSound( "NPC_Turret.Alert" );
	}
}
예제 #9
0
//=========================================================
// OnListened - monsters dig through the active sound list for
// any sounds that may interest them. (smells, too!)
//=========================================================
void CNPC_Bullsquid::OnListened( void )
{
	AISoundIter_t iter;
	
	CSound *pCurrentSound;

	static int conditionsToClear[] = 
	{
		COND_SQUID_SMELL_FOOD,
	};

	ClearConditions( conditionsToClear, ARRAYSIZE( conditionsToClear ) );
	
	pCurrentSound = GetSenses()->GetFirstHeardSound( &iter );
	
	while ( pCurrentSound )
	{
		// the npc cares about this sound, and it's close enough to hear.
		int condition = COND_NONE;
		
		if ( !pCurrentSound->FIsSound() )
		{
			// if not a sound, must be a smell - determine if it's just a scent, or if it's a food scent
			if ( pCurrentSound->IsSoundType( SOUND_MEAT | SOUND_CARCASS ) )
			{
				// the detected scent is a food item
				condition = COND_SQUID_SMELL_FOOD;
			}
		}
		
		if ( condition != COND_NONE )
			SetCondition( condition );

		pCurrentSound = GetSenses()->GetNextHeardSound( &iter );
	}

	BaseClass::OnListened();
}
예제 #10
0
//
// This search function will sit with the turret deployed and look for a new target. 
// After a set amount of time, the barrel will spin down. After m_flMaxWait, the turret will
// retact.
//
void CBaseTurret::SearchThink(void)
{
	// ensure rethink
	SetActivity( (Activity)ACT_TURRET_OPEN_IDLE );

	StudioFrameAdvance( );
	SetNextThink( gpGlobals->curtime + 0.1f );

	Ping( );

	// If we have a target and we're still healthy
	if (GetEnemy() != NULL)
	{
		if (!GetEnemy()->IsAlive() )
			SetEnemy( NULL );// Dead enemy forces a search for new one
	}

	// Acquire Target
	if (GetEnemy() == NULL)
	{
		GetSenses()->Look(TURRET_RANGE);
		SetEnemy( BestEnemy() );
	}

	// If we've found a target, spin up the barrel and start to attack
	if (GetEnemy() != NULL)
	{
		m_flLastSight = 0;
		SetThink(ActiveThink);
	}
	else
	{
		// Are we out of time, do we need to retract?
 		if (gpGlobals->curtime > m_flLastSight)
		{
			//Before we retrace, make sure that we are spun down.
			m_flLastSight = 0;
			SetThink(Retire);
		}
		
		// generic hunt for new victims
		m_vecGoalAngles.y = (m_vecGoalAngles.y + 0.1 * m_iBaseTurnRate);
		if (m_vecGoalAngles.y >= 360)
			m_vecGoalAngles.y -= 360;

		MoveTurret();
	}
}
void CNPC_Cremator::Spawn()
{	
	Precache();
	SetModel( "models/cremator_test2.mdl" ); // a model with a bit of a tesselation (tesselated head and collar)
	
	SetHullType(HULL_HUMAN); // отключено, т.к. это новый введенный тип хулла, и вряд ли есть смысл возиться с его введением. 

	//SetHullType( HULL_MEDIUM_TALL ); // данный стандартный тип подходит для большинства ситуаций, однако крематор не сможет проходить в двери, если они ненамного выше его
	
	/*that hull type is made special for a cremator since HULL_MEDIUM is not high enough but HULL_LARGE is too big
	and a cremator cannot fit in Borealis maps with it. The hull type is enumarated in ai_hull.h under the HULL_MEDIUM_TALL
	and then MUST be enumeraten in Hull_Bits_t, again, UNDER HULL_MEDIUM_TALL with the adress of 0x00000400,
	so that the definitions in ai_hull.cpp will then find the corresponding hull type.*/
	SetHullSizeNormal();
	
	SetBodygroup( 1, 0 ); // the gun
	SetBodygroup( 2, 0 ); // the head

	SetSolid( SOLID_BBOX );
	AddSolidFlags( FSOLID_NOT_STANDABLE );
	SetMoveType( MOVETYPE_STEP );

	m_bloodColor		= DONT_BLEED;//BLOOD_COLOR_YELLOW;
	m_iHealth			= sk_cremator_health.GetFloat();
	m_flFieldOfView		= VIEW_FIELD_WIDE;// indicates the width of this NPC's forward view cone ( as a dotproduct result )
	m_NPCState			= NPC_STATE_NONE;
	m_nSkin				= CREMATOR_SKIN_ALERT; // original yellow-eyes skin // Если надо спаунить крематора с иным цветом глаз, подставь значение из npc_cremator_h. 
	m_iAmmo				= m_iMaxAmmo = 54;

	NPCInit();

	m_flDistTooFar		= 6000.0;
	GetSenses()->SetDistLook( 6000.0 -1 );
	
	m_flNextIdleSoundTime	= gpGlobals->curtime; // + random->RandomFloat( 14.0f, 28.0f );
	m_flNextRangeAttack2Time = gpGlobals->curtime; // + random->RandomFloat( 10.0f, 20.0f );

	m_MuzzleAttachment	= LookupAttachment( "muzzle" );
	m_HeadAttachment	= LookupAttachment( "headattachment" );
		
	CapabilitiesAdd( bits_CAP_MOVE_GROUND | bits_CAP_TURN_HEAD );
	CapabilitiesAdd( bits_CAP_INNATE_RANGE_ATTACK1 ); // flamethrower
	CapabilitiesAdd( bits_CAP_INNATE_RANGE_ATTACK2 );

	CapabilitiesAdd( bits_CAP_MOVE_SHOOT ); // TODO: Melee?
}
//------------------------------------------------------------------------------
// Updates the enemy
//------------------------------------------------------------------------------
void CBaseHelicopter::UpdateEnemy()
{
	if( HasCondition( COND_ENEMY_DEAD ) )
	{
		SetEnemy( NULL );
	}

	// Look for my best enemy. If I change enemies, 
	// be sure and change my prevseen/lastseen timers.
	if( m_lifeState == LIFE_ALIVE )
	{
		GetSenses()->Look( (int)EnemySearchDistance() );

		GetEnemies()->RefreshMemories();
		ChooseEnemy();

		if( HasEnemy() )
		{
			CBaseEntity *pEnemy = GetEnemy();
			GatherEnemyConditions( pEnemy );
			if ( FVisible( pEnemy ) )
			{
				if (m_flLastSeen < gpGlobals->curtime - 2)
				{
					m_flPrevSeen = gpGlobals->curtime;
				}

				m_flLastSeen = gpGlobals->curtime;
				m_vecTargetPosition = pEnemy->WorldSpaceCenter();
			}
		}
		else
		{
			// look at where we're going instead
			m_vecTargetPosition = GetDesiredPosition();
		}
	}
	else
	{
		// If we're dead or dying, forget our enemy and don't look for new ones(sjb)
		SetEnemy( NULL );
	}

}
예제 #13
0
//------------------------------------------------------------------------------
// Purpose : Override base class to check range and visibility
// Input   :
// Output  :
//------------------------------------------------------------------------------
bool CNPC_EnemyFinder::IsValidEnemy( CBaseEntity *pTarget )
{
	float flTargetDist = GetAbsOrigin().DistTo( pTarget->GetAbsOrigin() );
	if (flTargetDist < m_flMinSearchDist)
		return false;

	if ( m_flMaxSearchDist && flTargetDist > m_flMaxSearchDist)
		return false;

	if ( !FBitSet( m_spawnflags, SF_ENEMY_FINDER_CHECK_VIS) )
		return true;

	if ( GetSenses()->DidSeeEntity( pTarget ) )
		return true;

	// Trace from launch position to target position.  
	// Use position above actual barral based on vertical launch speed
	Vector vStartPos = GetAbsOrigin();
	Vector vEndPos	 = pTarget->EyePosition();

	// Test our line of sight to the target
	trace_t tr;
	AI_TraceLOS( vStartPos, vEndPos, this, &tr );

	// If the player is in a vehicle, see if we can see that instead
	if ( pTarget->IsPlayer() )
	{
		CBasePlayer *pPlayer = assert_cast<CBasePlayer*>(pTarget);
		if ( tr.m_pEnt == pPlayer->GetVehicleEntity() )
			return true;
	}

	// Line must be clear
	if ( tr.fraction == 1.0f || tr.m_pEnt == pTarget )
		return true;

	// Otherwise we can't see anything
	return false;
}
예제 #14
0
//=========================================================
// AutoSearchThink - 
//=========================================================
void CNPC_BaseTurret::AutoSearchThink(void)
{
	// ensure rethink
	StudioFrameAdvance( );
	SetNextThink( gpGlobals->curtime + 0.3 );

	// If we have a target and we're still healthy

	CBaseEntity *pEnemy = GetEnemy();

	if (pEnemy != NULL)
	{
		if (!pEnemy->IsAlive() )
		{
			pEnemy = NULL;	
		}
	}

	// Acquire Target

	if (pEnemy == NULL)
	{
		GetSenses()->Look( TURRET_RANGE );
		pEnemy = BestEnemy();

		if ( pEnemy && !FVisible( pEnemy ) )
			 pEnemy = NULL;
	}

	if (pEnemy != NULL)
	{
		SetThink(&CNPC_BaseTurret::Deploy);
		CPASAttenuationFilter filter( this );
		EmitSound( filter, entindex(), "Turret.Alert" );
	}

	SetEnemy( pEnemy );
}
예제 #15
0
//-----------------------------------------------------------------------------
// Purpose: 
//
//
//-----------------------------------------------------------------------------
void CNPC_Stalker::Spawn( void )
{
	Precache( );

	SetModel( "models/stalker.mdl" );
	SetHullType(HULL_HUMAN);
	SetHullSizeNormal();

	SetSolid( SOLID_BBOX );
	AddSolidFlags( FSOLID_NOT_STANDABLE );
	SetMoveType( MOVETYPE_STEP );
	m_bloodColor		= DONT_BLEED;
	m_iHealth			= sk_stalker_health.GetFloat();
	m_flFieldOfView		= 0.1;// indicates the width of this NPC's forward view cone ( as a dotproduct result )
	m_NPCState			= NPC_STATE_NONE;
	CapabilitiesAdd( bits_CAP_SQUAD | bits_CAP_MOVE_GROUND );
	CapabilitiesAdd( bits_CAP_INNATE_RANGE_ATTACK1);

	m_flNextAttackSoundTime		= 0;
	m_flNextBreatheSoundTime	= gpGlobals->curtime + random->RandomFloat( 0.0, 10.0 );
	m_flNextScrambleSoundTime	= 0;
	m_nextSmokeTime = 0;
	m_bPlayingHitWall			= false;
	m_bPlayingHitFlesh			= false;

	m_fBeamEndTime				= 0;
	m_fBeamRechargeTime			= 0;
	m_fNextDamageTime			= 0;

	NPCInit();

	m_flDistTooFar	= MAX_STALKER_FIRE_RANGE;

	m_iPlayerAggression = 0;

	GetSenses()->SetDistLook(MAX_STALKER_FIRE_RANGE - 1);
}
예제 #16
0
//-----------------------------------------------------------------------------
// Purpose: This used to have something to do with bees flying, but 
//			now it only initializes moving furniture in scripted sequences
//-----------------------------------------------------------------------------
void CNPC_Furniture::Spawn( )
{
	Precache();
	
	SetModel( STRING(GetModelName()) );

	SetMoveType( MOVETYPE_STEP );
	SetSolid( SOLID_BBOX );

	// Our collision, if needed, will be done through bone followers
	AddSolidFlags( FSOLID_NOT_SOLID );

	SetBloodColor( DONT_BLEED );
	m_iHealth = TOO_MUCH_HEALTH_TO_DIE; //wow
	m_takedamage = DAMAGE_AIM;
	SetSequence( 0 );
	SetCycle( 0 );
	SetNavType( NAV_FLY );
	AddFlag( FL_FLY );

	CapabilitiesAdd( bits_CAP_MOVE_FLY | bits_CAP_TURN_HEAD | bits_CAP_ANIMATEDFACE );

	AddEFlags( EFL_NO_MEGAPHYSCANNON_RAGDOLL );

//	pev->nextthink += 1.0;
//	SetThink (WalkMonsterDelay);

	ResetSequenceInfo( );
	SetCycle( 0 );
	NPCInit();

	// Furniture needs to block LOS
	SetBlocksLOS( true );

	// Furniture just wastes CPU doing sensing code, since all they do is idle and play scripts
	GetSenses()->AddSensingFlags( SENSING_FLAGS_DONT_LOOK | SENSING_FLAGS_DONT_LISTEN );
}
예제 #17
0
//=========================================================
// SearchThink 
// This search function will sit with the turret deployed and look for a new target. 
// After a set amount of time, the barrel will spin down. After m_flMaxWait, the turret will
// retact.
//=========================================================
void CNPC_BaseTurret::SearchThink(void)
{
	// ensure rethink
	SetTurretAnim(TURRET_ANIM_SPIN);
	StudioFrameAdvance( );
	SetNextThink( gpGlobals->curtime + 0.1 );

	if (m_flSpinUpTime == 0 && m_flMaxSpin)
		m_flSpinUpTime = gpGlobals->curtime + m_flMaxSpin;

	Ping( );

	CBaseEntity *pEnemy = GetEnemy();

	// If we have a target and we're still healthy
	if (pEnemy != NULL)
	{
		if (!pEnemy->IsAlive() )
			pEnemy = NULL;// Dead enemy forces a search for new one
	}


	// Acquire Target
	if (pEnemy == NULL)
	{
		GetSenses()->Look(TURRET_RANGE);
		pEnemy = BestEnemy();

		if ( pEnemy && !FVisible( pEnemy ) )
			 pEnemy = NULL;
	}

	// If we've found a target, spin up the barrel and start to attack
	if (pEnemy != NULL)
	{
		m_flLastSight = 0;
		m_flSpinUpTime = 0;
		SetThink(&CNPC_BaseTurret::ActiveThink);
	}
	else
	{
		// Are we out of time, do we need to retract?
 		if (gpGlobals->curtime > m_flLastSight)
		{
			//Before we retrace, make sure that we are spun down.
			m_flLastSight = 0;
			m_flSpinUpTime = 0;
			SetThink(&CNPC_BaseTurret::Retire);
		}
		// should we stop the spin?
		else if ((m_flSpinUpTime) && (gpGlobals->curtime > m_flSpinUpTime))
		{
			SpinDownCall();
		}
		
		// generic hunt for new victims
		m_vecGoalAngles.y = (m_vecGoalAngles.y + 0.1 * m_fTurnRate);
		if (m_vecGoalAngles.y >= 360)
			m_vecGoalAngles.y -= 360;
		MoveTurret();
	}

	SetEnemy( pEnemy );
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CNPC_Monk::GatherConditions()
{
	BaseClass::GatherConditions();

	// Build my zombie danger index!
	m_iNumZombies = 0;
	m_iDangerousZombies = 0;

	AISightIter_t iter;
	CBaseEntity *pSightEnt;
	pSightEnt = GetSenses()->GetFirstSeenEntity( &iter );
	while( pSightEnt )
	{
		if( pSightEnt->Classify() == CLASS_ZOMBIE && pSightEnt->IsAlive() )
		{
			// Is this zombie coming for me?
			CAI_BaseNPC *pZombie = dynamic_cast<CAI_BaseNPC*>(pSightEnt);
			
			if( pZombie && pZombie->GetEnemy() == this )
			{
				m_iNumZombies++;

				// if this zombie is close enough to attack, add him to the zombie danger!
				float flDist;

				flDist = (pZombie->GetAbsOrigin() - GetAbsOrigin()).Length2DSqr();

				if( flDist <= 128.0f * 128.0f )
				{
					m_iDangerousZombies++;
				}
			}
		}

		pSightEnt = GetSenses()->GetNextSeenEntity( &iter );
	}

	if( m_iDangerousZombies >= 3 || (GetEnemy() && GetHealth() < 25) )
	{
		// I see many zombies, or I'm quite injured.
		SpeakIfAllowed( TLK_HELP_ME );
	}

	// NOTE!!!!!! This code assumes grigori is using annabelle!
	ClearCondition(COND_LOW_PRIMARY_AMMO);
	if ( GetActiveWeapon() )
	{
		if ( GetActiveWeapon()->UsesPrimaryAmmo() )
		{
			if (!GetActiveWeapon()->HasPrimaryAmmo() )
			{
				SetCondition(COND_NO_PRIMARY_AMMO);
			}
			else if ( m_NPCState != NPC_STATE_COMBAT && GetActiveWeapon()->UsesClipsForAmmo1() && GetActiveWeapon()->Clip1() < 2 )
			{
				// Don't send a low ammo message unless we're not in combat.
				SetCondition(COND_LOW_PRIMARY_AMMO);
			}
		}
	}
}
예제 #19
0
void CSnark::HuntThink( void )
{
	if (!IsInWorld())
	{
		SetTouch( NULL );
		UTIL_Remove( this );
		return;
	}
	
	StudioFrameAdvance( );
	SetNextThink( gpGlobals->curtime + 0.1f );

	// explode when ready
	if ( gpGlobals->curtime >= m_flDie )
	{
		g_vecAttackDir = GetAbsVelocity();
		VectorNormalize( g_vecAttackDir );
		m_iHealth = -1;
		CTakeDamageInfo	info( this, this, 1, DMG_GENERIC );
		Event_Killed( info );
		return;
	}

	// float
	if ( GetWaterLevel() != 0)
	{
		if ( GetMoveType() == MOVETYPE_FLYGRAVITY )
		{
			SetMoveType( MOVETYPE_FLY, MOVECOLLIDE_FLY_BOUNCE );
		}

		Vector vecVel = GetAbsVelocity();
		vecVel *= 0.9;
		vecVel.z += 8.0;
		SetAbsVelocity( vecVel );
	}
	else if ( GetMoveType() == MOVETYPE_FLY )
	{
		SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE );
	}

	// return if not time to hunt
	if ( m_flNextHunt > gpGlobals->curtime )
		return;

	m_flNextHunt = gpGlobals->curtime + 2.0;
	
	Vector vecFlat = GetAbsVelocity();
	vecFlat.z = 0;
	VectorNormalize( vecFlat );

	if ( GetEnemy() == NULL || !GetEnemy()->IsAlive() )
	{
		// find target, bounce a bit towards it.
		GetSenses()->Look( 512 );
		SetEnemy( BestEnemy() );
	}

	// squeek if it's about time blow up
	if ( (m_flDie - gpGlobals->curtime <= 0.5) && (m_flDie - gpGlobals->curtime >= 0.3) )
	{
		CPASAttenuationFilter filter( this );
		enginesound->EmitSound( filter, entindex(), CHAN_VOICE, "squeek/sqk_die1.wav", 1, ATTN_NORM, 0, 100 + random->RandomInt( 0, 0x3F ) );
		CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), 256, 0.25 );
	}

	// higher pitch as squeeker gets closer to detonation time
	float flpitch = 155.0 - 60.0 * ( (m_flDie - gpGlobals->curtime) / SQUEEK_DETONATE_DELAY );
	if ( flpitch < 80 )
		flpitch = 80;

	if ( GetEnemy() != NULL )
	{
		if ( FVisible( GetEnemy() ) )
		{
			m_vecTarget = GetEnemy()->EyePosition() - GetAbsOrigin();
			VectorNormalize( m_vecTarget );
		}

		float flVel = GetAbsVelocity().Length();
		float flAdj = 50.0 / ( flVel + 10.0 );

		if ( flAdj > 1.2 )
			flAdj = 1.2;
		
		// ALERT( at_console, "think : enemy\n");

		// ALERT( at_console, "%.0f %.2f %.2f %.2f\n", flVel, m_vecTarget.x, m_vecTarget.y, m_vecTarget.z );

		SetAbsVelocity( GetAbsVelocity() * flAdj + (m_vecTarget * 300) );
	}

	if ( GetFlags() & FL_ONGROUND )
	{
		SetLocalAngularVelocity( QAngle( 0, 0, 0 ) );
	}
	else
	{
		QAngle angVel = GetLocalAngularVelocity();
		if ( angVel == QAngle( 0, 0, 0 ) )
		{
			angVel.x = random->RandomFloat( -100, 100 );
			angVel.z = random->RandomFloat( -100, 100 );
			SetLocalAngularVelocity( angVel );
		}
	}

	if ( ( GetAbsOrigin() - m_posPrev ).Length() < 1.0 )
	{
		Vector vecVel = GetAbsVelocity();
		vecVel.x = random->RandomFloat( -100, 100 );
		vecVel.y = random->RandomFloat( -100, 100 );
		SetAbsVelocity( vecVel );
	}

	m_posPrev = GetAbsOrigin();

	QAngle angles;
	VectorAngles( GetAbsVelocity(), angles );
	angles.z = 0;
	angles.x = 0;
	SetAbsAngles( angles );
}
void CNPC_Tentacle::Cycle( void )
{
	//NDebugOverlay::Cross3D( EarPosition(), 32, 255, 0, 0, false, 0.1 );

	// ALERT( at_console, "%s %.2f %d %d\n", STRING( pev->targetname ), pev->origin.z, m_MonsterState, m_IdealMonsterState );
	SetNextThink( gpGlobals->curtime + 0.1 );

	// ALERT( at_console, "%s %d %d %d %f %f\n", STRING( pev->targetname ), pev->sequence, m_iGoalAnim, m_iDir, pev->framerate, pev->health );

	if ( m_NPCState == NPC_STATE_SCRIPT || GetIdealState() == NPC_STATE_SCRIPT)
	{
		SetAbsAngles( QAngle( GetAbsAngles().x, m_flInitialYaw, GetAbsAngles().z ) );
		GetMotor()->SetIdealYaw( m_flInitialYaw );	
		RemoveIgnoredConditions();
		NPCThink( );
		m_iGoalAnim = TENTACLE_ANIM_Pit_Idle;
		return;
	}
	
	StudioFrameAdvance();
	DispatchAnimEvents( this );

	GetMotor()->UpdateYaw( MaxYawSpeed() );
	
	CSound *pSound = NULL;

	GetSenses()->Listen();
	m_BoneFollowerManager.UpdateBoneFollowers(this);

	// Listen will set this if there's something in my sound list
	if ( HeardAnything() )
		 pSound = GetSenses()->GetClosestSound( false, (SOUND_DANGER|SOUND_COMBAT|SOUND_WORLD|SOUND_PLAYER) );
	else
		 pSound = NULL;

	if ( pSound )
	{
		//NDebugOverlay::Line( EarPosition(), pSound->GetSoundOrigin(), 0, 255, 0, false, 0.2 );

		Vector vecDir;
		if ( gpGlobals->curtime - m_flPrevSoundTime < 0.5 )
		{
			float dt = gpGlobals->curtime - m_flPrevSoundTime;
			vecDir = pSound->GetSoundOrigin() + (pSound->GetSoundOrigin() - m_vecPrevSound) / dt - GetAbsOrigin();
		}
		else
		{
			vecDir = pSound->GetSoundOrigin() - GetAbsOrigin();
		}

		m_flPrevSoundTime = gpGlobals->curtime;
		m_vecPrevSound = pSound->GetSoundOrigin();

		m_flSoundYaw = VecToYaw ( vecDir ) - m_flInitialYaw;

		m_iSoundLevel = Level( vecDir.z );

		if (m_flSoundYaw < -180)
			m_flSoundYaw += 360;
		if (m_flSoundYaw > 180)
			m_flSoundYaw -= 360;

		// ALERT( at_console, "sound %d %.0f\n", m_iSoundLevel, m_flSoundYaw );
		if (m_flSoundTime < gpGlobals->curtime)
		{
			// play "I hear new something" sound
			 UTIL_EmitAmbientSound( GetSoundSourceIndex(), GetAbsOrigin() + Vector( 0, 0, MyHeight()), "Tentacle.Alert", 1.0, SNDLVL_GUNFIRE, 0, 100);
		}
		m_flSoundTime = gpGlobals->curtime + random->RandomFloat( 5.0, 10.0 );
	}

	// clip ideal_yaw
	float dy = m_flSoundYaw;
	switch( GetSequence() )
	{
	case TENTACLE_ANIM_Floor_Rear:
	case TENTACLE_ANIM_Floor_Rear_Idle:
	case TENTACLE_ANIM_Lev1_Rear:
	case TENTACLE_ANIM_Lev1_Rear_Idle:
	case TENTACLE_ANIM_Lev2_Rear:
	case TENTACLE_ANIM_Lev2_Rear_Idle:
	case TENTACLE_ANIM_Lev3_Rear:
	case TENTACLE_ANIM_Lev3_Rear_Idle:
		if (dy < 0 && dy > -m_flMaxYaw)
			dy = -m_flMaxYaw;
		if (dy > 0 && dy < m_flMaxYaw)
			dy = m_flMaxYaw;
		break;
	default:
		if (dy < -m_flMaxYaw)
			dy = -m_flMaxYaw;
		if (dy > m_flMaxYaw)
			dy = m_flMaxYaw;
	}
	GetMotor()->SetIdealYaw( m_flInitialYaw + dy );

	if ( IsSequenceFinished() )
	{
		// ALERT( at_console, "%s done %d %d\n", STRING( pev->targetname ), pev->sequence, m_iGoalAnim );
		if ( m_iHealth <= 1)
		{
			m_iGoalAnim = TENTACLE_ANIM_Pit_Idle;

			if ( GetSequence() == TENTACLE_ANIM_Pit_Idle)
			{
				m_iHealth = 75;
			}
		}
		else if ( m_flSoundTime > gpGlobals->curtime )
		{
			if (m_flSoundYaw >= -(m_flMaxYaw + 30) && m_flSoundYaw <= (m_flMaxYaw + 30))
			{
				// strike
				switch ( m_iSoundLevel )
				{
					case 0:
						m_iGoalAnim = SelectWeightedSequence ( ACT_1030 );
						break;
					case 1:
						m_iGoalAnim = SelectWeightedSequence ( ACT_1031 );
						break;
					case 2:
						m_iGoalAnim = SelectWeightedSequence ( ACT_1032 );
						break;
					case 3:
						m_iGoalAnim = SelectWeightedSequence ( ACT_1033 );
						break;
				}
			}
			else if (m_flSoundYaw >= -m_flMaxYaw * 2 && m_flSoundYaw <= m_flMaxYaw * 2) 
			{
				// tap
				switch ( m_iSoundLevel )
				{
					case 0:
						m_iGoalAnim = SelectWeightedSequence ( ACT_1020 );
						break;
					case 1:
						m_iGoalAnim = SelectWeightedSequence ( ACT_1021 );
						break;
					case 2:
						m_iGoalAnim = SelectWeightedSequence ( ACT_1022 );
						break;
					case 3:
						m_iGoalAnim = SelectWeightedSequence ( ACT_1023 );
						break;
				}
			}
			else
			{
				// go into rear idle
				switch ( m_iSoundLevel )
				{
					case 0:
						m_iGoalAnim = SelectWeightedSequence ( ACT_1040 );
						break;
					case 1:
						m_iGoalAnim = SelectWeightedSequence ( ACT_1041 );
						break;
					case 2:
						m_iGoalAnim = SelectWeightedSequence ( ACT_1042 );
						break;
					case 3:
						m_iGoalAnim = SelectWeightedSequence ( ACT_1043 );
						break;
					case 4:
						m_iGoalAnim = SelectWeightedSequence ( ACT_1044 );
						break;
				}
			}
		}
		else if ( GetSequence() == TENTACLE_ANIM_Pit_Idle)
		{
			// stay in pit until hear noise
			m_iGoalAnim = TENTACLE_ANIM_Pit_Idle;
		}
		else if ( GetSequence() == m_iGoalAnim)
		{
			if ( MyLevel() >= 0 && gpGlobals->curtime < m_flSoundTime)
			{
				if ( random->RandomInt(0,9) < m_flSoundTime - gpGlobals->curtime )
				{
					// continue stike
					switch ( m_iSoundLevel )
					{
						case 0:
							m_iGoalAnim = SelectWeightedSequence ( ACT_1030 );
							break;
						case 1:
							m_iGoalAnim = SelectWeightedSequence ( ACT_1031 );
							break;
						case 2:
							m_iGoalAnim = SelectWeightedSequence ( ACT_1032 );
							break;
						case 3:
							m_iGoalAnim = SelectWeightedSequence ( ACT_1033 );
							break;
					}
				}
				else
				{
					// tap
					switch ( m_iSoundLevel )
					{
						case 0:
							m_iGoalAnim = SelectWeightedSequence ( ACT_1020 );
							break;
						case 1:
							m_iGoalAnim = SelectWeightedSequence ( ACT_1021 );
							break;
						case 2:
							m_iGoalAnim = SelectWeightedSequence ( ACT_1022 );
							break;
						case 3:
							m_iGoalAnim = SelectWeightedSequence ( ACT_1023 );
							break;
					}
				}
			}
			else if ( MyLevel( ) < 0 )
			{
				m_iGoalAnim = SelectWeightedSequence( ACT_1010 );
			}
			else
			{
				if (m_flNextSong < gpGlobals->curtime)
				{
					// play "I hear new something" sound
					CPASAttenuationFilter filter( this );
					EmitSound( filter, entindex(), "Tentacle.Sing" );

					m_flNextSong = gpGlobals->curtime + random->RandomFloat( 10, 20 );
				}

				if (random->RandomInt(0,15) == 0)
				{
					// idle on new level
					switch ( random->RandomInt( 0, 3 ) )
					{
						case 0:
							m_iGoalAnim = SelectWeightedSequence ( ACT_1010 );
							break;
						case 1:
							m_iGoalAnim = SelectWeightedSequence ( ACT_1011 );
							break;
						case 2:
							m_iGoalAnim = SelectWeightedSequence ( ACT_1012 );
							break;
						case 3:
							m_iGoalAnim = SelectWeightedSequence ( ACT_1013 );
							break;
					}
				}
				else if ( random->RandomInt( 0, 3 ) == 0 )
				{
					// tap
					switch ( MyLevel() )
					{
						case 0:
							m_iGoalAnim = SelectWeightedSequence ( ACT_1020 );
							break;
						case 1:
							m_iGoalAnim = SelectWeightedSequence ( ACT_1021 );
							break;
						case 2:
							m_iGoalAnim = SelectWeightedSequence ( ACT_1022 );
							break;
						case 3:
							m_iGoalAnim = SelectWeightedSequence ( ACT_1023 );
							break;
					}
				}
				else
				{
					// idle
					switch ( MyLevel() )
					{
						case 0:
							m_iGoalAnim = SelectWeightedSequence ( ACT_1010 );
							break;
						case 1:
							m_iGoalAnim = SelectWeightedSequence ( ACT_1011 );
							break;
						case 2:
							m_iGoalAnim = SelectWeightedSequence ( ACT_1012 );
							break;
						case 3:
							m_iGoalAnim = SelectWeightedSequence ( ACT_1013 );
							break;
					}
				}
			}
			if (m_flSoundYaw < 0)
				m_flSoundYaw += random->RandomFloat( 2, 8 );
			else
				m_flSoundYaw -= random->RandomFloat( 2, 8 );
		}

		SetSequence( FindTransitionSequence( GetSequence(), m_iGoalAnim, &m_iDir ) );
		

		if (m_iDir > 0)
		{
			SetCycle( 0 );
		}
		else
		{
			m_iDir = -1; // just to safe
			SetCycle( 1.0f );
		}

		ResetSequenceInfo( );

		m_flFramerateAdj = random->RandomFloat( -0.2, 0.2 );
		m_flPlaybackRate = m_iDir * 1.0 + m_flFramerateAdj;

		switch( GetSequence() )
		{
		case TENTACLE_ANIM_Floor_Tap:
		case TENTACLE_ANIM_Lev1_Tap:
		case TENTACLE_ANIM_Lev2_Tap:
		case TENTACLE_ANIM_Lev3_Tap:
			{
				Vector vecSrc, v_forward;
				AngleVectors( GetAbsAngles(), &v_forward );

				trace_t tr1, tr2;

				vecSrc = GetAbsOrigin() + Vector( 0, 0, MyHeight() - 4);
				UTIL_TraceLine( vecSrc, vecSrc + v_forward * 512, MASK_NPCSOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr1 );

				vecSrc = GetAbsOrigin() + Vector( 0, 0, MyHeight() + 8);
				UTIL_TraceLine( vecSrc, vecSrc + v_forward * 512, MASK_NPCSOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr2 );

				// ALERT( at_console, "%f %f\n", tr1.flFraction * 512, tr2.flFraction * 512 );

				m_flTapRadius = SetPoseParameter( 0, random->RandomFloat( tr1.fraction * 512, tr2.fraction * 512 ) );
			}
			break;
		default:
			m_flTapRadius = 336; // 400 - 64
			break;
		}
		SetViewOffset( Vector( 0, 0, MyHeight() ) );
		// ALERT( at_console, "seq %d\n", pev->sequence );
	}

	if (m_flPrevSoundTime + 2.0 > gpGlobals->curtime)
	{
		// 1.5 normal speed if hears sounds
		m_flPlaybackRate = m_iDir * 1.5 + m_flFramerateAdj;
	}
	else if (m_flPrevSoundTime + 5.0 > gpGlobals->curtime)
	{
		// slowdown to normal
		m_flPlaybackRate = m_iDir + m_iDir * (5 - (gpGlobals->curtime - m_flPrevSoundTime)) / 2 + m_flFramerateAdj;
	}
}
예제 #21
0
//-----------------------------------------------------------------------------
// Purpose: Target doesn't exist or has eluded us, so search for one
//-----------------------------------------------------------------------------
void CNPC_CeilingTurret::SearchThink( void )
{
	//Allow descended classes a chance to do something before the think function
	if ( PreThink( TURRET_SEARCHING ) )
		return;

	SetNextThink( gpGlobals->curtime + 0.05f );

	SetActivity( (Activity) ACT_CEILING_TURRET_OPEN_IDLE );

	//If our enemy has died, pick a new enemy
	if ( ( GetEnemy() != NULL ) && ( GetEnemy()->IsAlive() == false ) )
	{
		SetEnemy( NULL );
	}

	//Acquire the target
 	if ( GetEnemy() == NULL )
	{
		GetSenses()->Look( CEILING_TURRET_RANGE );
		CBaseEntity *pEnemy = BestEnemy();
		if ( pEnemy )
		{
			SetEnemy( pEnemy );
		}
	}

	//If we've found a target, spin up the barrel and start to attack
	if ( GetEnemy() != NULL )
	{
		//Give players a grace period
		if ( GetEnemy()->IsPlayer() )
		{
			m_flShotTime  = gpGlobals->curtime + 0.5f;
		}
		else
		{
			m_flShotTime  = gpGlobals->curtime + 0.1f;
		}

		m_flLastSight = 0;
		SetThink( &CNPC_CeilingTurret::ActiveThink );
		SetEyeState( TURRET_EYE_SEE_TARGET );

		SpinUp();
		EmitSound( "NPC_CeilingTurret.Active" );
		return;
	}

	//Are we out of time and need to retract?
 	if ( gpGlobals->curtime > m_flLastSight )
	{
		//Before we retrace, make sure that we are spun down.
		m_flLastSight = 0;
		SetThink( &CNPC_CeilingTurret::Retire );
		return;
	}
	
	//Display that we're scanning
	m_vecGoalAngles.x = 15.0f;
	m_vecGoalAngles.y = GetAbsAngles().y + ( sin( gpGlobals->curtime * 2.0f ) * 45.0f );

	//Turn and ping
	UpdateFacing();
	Ping();
}
//=========================================================
// Hornet is flying, gently tracking target
//=========================================================
void CNPC_Hornet::TrackTarget ( void )
{
	Vector	vecFlightDir;
	Vector	vecDirToEnemy;
	float	flDelta;

	StudioFrameAdvance( );

	if (gpGlobals->curtime > m_flStopAttack)
	{
		SetTouch( NULL );
		SetThink( &CBaseEntity::SUB_Remove );
		SetNextThink( gpGlobals->curtime + 0.1f );
		return;
	}

	// UNDONE: The player pointer should come back after returning from another level
	if ( GetEnemy() == NULL )
	{// enemy is dead.
		GetSenses()->Look( 1024 );
		SetEnemy( BestEnemy() );
	}
	
	if ( GetEnemy() != NULL && FVisible( GetEnemy() ))
	{
		m_vecEnemyLKP = GetEnemy()->BodyTarget( GetAbsOrigin() );
	}
	else
	{
		m_vecEnemyLKP = m_vecEnemyLKP + GetAbsVelocity() * m_flFlySpeed * 0.1;
	}

	vecDirToEnemy = m_vecEnemyLKP - GetAbsOrigin();
	VectorNormalize( vecDirToEnemy );

	if ( GetAbsVelocity().Length() < 0.1 )
		vecFlightDir = vecDirToEnemy;
	else 
	{
		vecFlightDir = GetAbsVelocity();
		VectorNormalize( vecFlightDir );
	}

	SetAbsVelocity( vecFlightDir + vecDirToEnemy );

	// measure how far the turn is, the wider the turn, the slow we'll go this time.
	flDelta = DotProduct ( vecFlightDir, vecDirToEnemy );
	
	if ( flDelta < 0.5 )
	{// hafta turn wide again. play sound
		CPASAttenuationFilter filter( this );
		EmitSound( filter, entindex(), "Hornet.Buzz" );
	}

	if ( flDelta <= 0 && m_iHornetType == HORNET_TYPE_RED )
	{// no flying backwards, but we don't want to invert this, cause we'd go fast when we have to turn REAL far.
		flDelta = 0.25;
	}

	Vector vecVel = vecFlightDir + vecDirToEnemy;
	VectorNormalize( vecVel );

	if ( GetOwnerEntity() && (GetOwnerEntity()->GetFlags() & FL_NPC) )
	{
		// random pattern only applies to hornets fired by monsters, not players. 

		vecVel.x += random->RandomFloat ( -0.10, 0.10 );// scramble the flight dir a bit.
		vecVel.y += random->RandomFloat ( -0.10, 0.10 );
		vecVel.z += random->RandomFloat ( -0.10, 0.10 );
	}
	
	switch ( m_iHornetType )
	{
		case HORNET_TYPE_RED:
			SetAbsVelocity( vecVel * ( m_flFlySpeed * flDelta ) );// scale the dir by the ( speed * width of turn )
			SetNextThink( gpGlobals->curtime + random->RandomFloat( 0.1, 0.3 ) );
			break;
		default:
			Assert( false );	//fall through if release
		case HORNET_TYPE_ORANGE:
			SetAbsVelocity( vecVel * m_flFlySpeed );// do not have to slow down to turn.
			SetNextThink( gpGlobals->curtime + 0.1f );// fixed think time
			break;
	}

	QAngle angNewAngles;
	VectorAngles( GetAbsVelocity(), angNewAngles );
	SetAbsAngles( angNewAngles );
	
	SetSolid( SOLID_BBOX );

	// if hornet is close to the enemy, jet in a straight line for a half second.
	// (only in the single player game)
	if ( GetEnemy() != NULL && !g_pGameRules->IsMultiplayer() )
	{
		if ( flDelta >= 0.4 && ( GetAbsOrigin() - m_vecEnemyLKP ).Length() <= 300 )
		{
			CPVSFilter filter( GetAbsOrigin() );
			te->Sprite( filter, 0.0,
				&GetAbsOrigin(), // pos
				iHornetPuff,	// model
				0.2,				//size
				128				// brightness
			);

			CPASAttenuationFilter filter2( this );
			EmitSound( filter2, entindex(), "Hornet.Buzz" );
			SetAbsVelocity( GetAbsVelocity() * 2 );
			SetNextThink( gpGlobals->curtime + 1.0f );
			// don't attack again
			m_flStopAttack = gpGlobals->curtime;
		}
	}
}
//------------------------------------------------------------------------------
// Purpose :
// Input   :
// Output  :
//------------------------------------------------------------------------------
void CBaseHelicopter::Hunt( void )
{
	UpdateTrackNavigation( );

	if( HasCondition( COND_ENEMY_DEAD ) )
	{
		SetEnemy( NULL );
	}

	// Look for my best enemy. If I change enemies, 
	// be sure and change my prevseen/lastseen timers.
	if( m_lifeState == LIFE_ALIVE )
	{
		GetSenses()->Look( 4092 );

		GetEnemies()->RefreshMemories();
		ChooseEnemy();

		if( HasEnemy() )
		{
			GatherEnemyConditions( GetEnemy() );

			if (FVisible( GetEnemy() ))
			{
				if (m_flLastSeen < gpGlobals->curtime - 2)
				{
					m_flPrevSeen = gpGlobals->curtime;
				}

				m_flLastSeen = gpGlobals->curtime;
				m_vecTargetPosition = GetEnemy()->WorldSpaceCenter();
			}
		}
		else
		{
			// look at where we're going instead
			m_vecTargetPosition = GetDesiredPosition();
		}
	}
	else
	{
		// If we're dead or dying, forget our enemy and don't look for new ones(sjb)
		SetEnemy( NULL );
	}

	if ( 1 )
	{
		Vector targetDir = m_vecTargetPosition - GetAbsOrigin();
		Vector desiredDir = GetDesiredPosition() - GetAbsOrigin();

		VectorNormalize( targetDir ); 
		VectorNormalize( desiredDir ); 

		if ( !IsCrashing() && m_flLastSeen + 5 > gpGlobals->curtime ) //&& DotProduct( targetDir, desiredDir) > 0.25)
		{
			// If we've seen the target recently, face the target.
			//Msg( "Facing Target \n" );
			m_vecDesiredFaceDir = targetDir;
		}
		else
		{
			// Face our desired position.
			// Msg( "Facing Position\n" );
			m_vecDesiredFaceDir = desiredDir;
		}
	}
	else
	{
		// Face the way the path corner tells us to.
		//Msg( "Facing my path corner\n" );
		m_vecDesiredFaceDir = GetGoalOrientation();
	}

	Flight();

	UpdatePlayerDopplerShift( );

	// ALERT( at_console, "%.0f %.0f %.0f\n", gpGlobals->curtime, m_flLastSeen, m_flPrevSeen );
	if (m_fHelicopterFlags & BITS_HELICOPTER_GUN_ON)
	{
		//if ( (m_flLastSeen + 1 > gpGlobals->curtime) && (m_flPrevSeen + 2 < gpGlobals->curtime) )
		{
			if (FireGun( ))
			{
				// slow down if we're firing
				if (m_flGoalSpeed > m_flMaxSpeedFiring )
				{
					m_flGoalSpeed = m_flMaxSpeedFiring;
				}
			}
		}
	}

	if (m_fHelicopterFlags & BITS_HELICOPTER_MISSILE_ON)
	{
		AimRocketGun();
	}

	// Finally, forget dead enemies.
	if( GetEnemy() != NULL && (!GetEnemy()->IsAlive() || GetEnemy()->GetFlags() & FL_NOTARGET) )
	{
		SetEnemy( NULL );
	}
}