text: //========= Copyright Valve Corporation, All rights reserved. ============//
  //
  // Purpose: 
  //
  // $NoKeywords: $
  //=============================================================================//
  
  #include "cbase.h"
  #include "npc_turret_floor.h"
  #include "ai_senses.h"
  #include "ai_memory.h"
  #include "engine/IEngineSound.h"
  #include "ammodef.h"
  #include "hl2/hl2_player.h"
  #include "soundenvelope.h"
  #include "physics_saverestore.h"
  #include "IEffects.h"
  #include "basehlcombatweapon_shared.h"
  #include "phys_controller.h"
  #include "ai_interactions.h"
  #include "Sprite.h"
  #include "beam_shared.h"
  #include "props.h"
  #include "particle_parse.h"
  
  #ifdef PORTAL
  	#include "prop_portal_shared.h"
  	#include "portal_util_shared.h"
  #endif
  
  // memdbgon must be the last include file in a .cpp file!!!
  #include "tier0/memdbgon.h"
  
  const char *GetMassEquivalent(float flMass);
  
  #define	DISABLE_SHOT	0
  
  //Debug visualization
  ConVar	g_debug_turret( "g_debug_turret", "0" );
  
  extern ConVar physcannon_tracelength;
  
  // Interactions
  int	g_interactionTurretStillStanding	= 0;
  
  float CNPC_FloorTurret::fMaxTipControllerVelocity = 300.0f * 300.0f;
  float CNPC_FloorTurret::fMaxTipControllerAngularVelocity = 90.0f * 90.0f;
  
  #define	LASER_BEAM_SPRITE			"effects/laser1.vmt"
  
  #define	FLOOR_TURRET_MODEL			"models/combine_turrets/floor_turret.mdl"
  #define	FLOOR_TURRET_MODEL_CITIZEN	"models/combine_turrets/citizen_turret.mdl"
  #define FLOOR_TURRET_GLOW_SPRITE	"sprites/glow1.vmt"
  // #define FLOOR_TURRET_BC_YAW			"aim_yaw"
  // #define FLOOR_TURRET_BC_PITCH		"aim_pitch"
  #define	FLOOR_TURRET_RANGE			1200
  #define	FLOOR_TURRET_MAX_WAIT		5
  #define FLOOR_TURRET_SHORT_WAIT		2.0		// Used for FAST_RETIRE spawnflag
  #define	FLOOR_TURRET_PING_TIME		1.0f	//LPB!!
  
  #define	FLOOR_TURRET_VOICE_PITCH_LOW	45
  #define	FLOOR_TURRET_VOICE_PITCH_HIGH	100
  
  //Aiming variables
  #define	FLOOR_TURRET_MAX_NOHARM_PERIOD		0.0f
  #define	FLOOR_TURRET_MAX_GRACE_PERIOD		3.0f
  
  //Activities
  int ACT_FLOOR_TURRET_OPEN;
  int ACT_FLOOR_TURRET_CLOSE;
  int ACT_FLOOR_TURRET_OPEN_IDLE;
  int ACT_FLOOR_TURRET_CLOSED_IDLE;
  int ACT_FLOOR_TURRET_FIRE;
  
  //Datatable
  BEGIN_DATADESC( CNPC_FloorTurret )
  
  	DEFINE_FIELD( m_iAmmoType,	FIELD_INTEGER ),
  	DEFINE_FIELD( m_bAutoStart,	FIELD_BOOLEAN ),
  	DEFINE_FIELD( m_bActive,		FIELD_BOOLEAN ),
  	DEFINE_FIELD( m_bBlinkState,	FIELD_BOOLEAN ),
  	DEFINE_FIELD( m_bEnabled,		FIELD_BOOLEAN ),
  	DEFINE_FIELD( m_bNoAlarmSounds,	FIELD_BOOLEAN ),
  
  	DEFINE_FIELD( m_flShotTime,	FIELD_TIME ),
  	DEFINE_FIELD( m_flLastSight,	FIELD_TIME ),
  	DEFINE_FIELD( m_flThrashTime,	FIELD_TIME ),
  	DEFINE_FIELD( m_flPingTime,	FIELD_TIME ),
  	DEFINE_FIELD( m_flNextActivateSoundTime, FIELD_TIME ),
  	DEFINE_FIELD( m_bCarriedByPlayer, FIELD_BOOLEAN ),
  	DEFINE_FIELD( m_bUseCarryAngles, FIELD_BOOLEAN ),
  	DEFINE_FIELD( m_flPlayerDropTime, FIELD_TIME ),
  	DEFINE_FIELD( m_hLastNPCToKickMe, FIELD_EHANDLE ),
  	DEFINE_FIELD( m_flKnockOverFailedTime, FIELD_TIME ),
  	DEFINE_FIELD( m_flDestructStartTime, FIELD_TIME ),
  	DEFINE_FIELD( m_hFizzleEffect, FIELD_EHANDLE ),
  
  	DEFINE_FIELD( m_vecGoalAngles,FIELD_VECTOR ),
  	DEFINE_FIELD( m_iEyeAttachment,	FIELD_INTEGER ),
  	DEFINE_FIELD( m_iMuzzleAttachment,	FIELD_INTEGER ),
  	DEFINE_FIELD( m_iEyeState,		FIELD_INTEGER ),
  	DEFINE_FIELD( m_hEyeGlow,		FIELD_EHANDLE ),
  	DEFINE_FIELD( m_pMotionController,FIELD_EHANDLE),
  	DEFINE_FIELD( m_vecEnemyLKP,	FIELD_VECTOR ),
  	DEFINE_FIELD( m_hLaser,			FIELD_EHANDLE ),
  	DEFINE_FIELD( m_bSelfDestructing,	FIELD_BOOLEAN ),
  
  	DEFINE_FIELD( m_hPhysicsAttacker, FIELD_EHANDLE ),
  	DEFINE_FIELD( m_flLastPhysicsInfluenceTime, FIELD_TIME ),
  
  	DEFINE_FIELD( m_bHackedByAlyx, FIELD_BOOLEAN ),
  
  	DEFINE_KEYFIELD( m_iKeySkin, FIELD_INTEGER, "SkinNumber" ),
  	
  	DEFINE_THINKFUNC( Retire ),
  	DEFINE_THINKFUNC( Deploy ),
  	DEFINE_THINKFUNC( ActiveThink ),
  	DEFINE_THINKFUNC( SearchThink ),
  	DEFINE_THINKFUNC( AutoSearchThink ),
  	DEFINE_THINKFUNC( TippedThink ),
  	DEFINE_THINKFUNC( InactiveThink ),
  	DEFINE_THINKFUNC( SuppressThink ),
  	DEFINE_THINKFUNC( DisabledThink ),
  	DEFINE_THINKFUNC( SelfDestructThink ),
  	DEFINE_THINKFUNC( BreakThink ),
  
  	DEFINE_USEFUNC( ToggleUse ),
  
  	// Inputs
  	DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ),
  	DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
  	DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
  	DEFINE_INPUTFUNC( FIELD_VOID, "DepleteAmmo", InputDepleteAmmo ),
  	DEFINE_INPUTFUNC( FIELD_VOID, "RestoreAmmo", InputRestoreAmmo ),
  	DEFINE_INPUTFUNC( FIELD_VOID, "SelfDestruct", InputSelfDestruct ),
  
  	DEFINE_OUTPUT( m_OnDeploy, "OnDeploy" ),
  	DEFINE_OUTPUT( m_OnRetire, "OnRetire" ),
  	DEFINE_OUTPUT( m_OnTipped, "OnTipped" ),
  	DEFINE_OUTPUT( m_OnPhysGunPickup, "OnPhysGunPickup" ),
  	DEFINE_OUTPUT( m_OnPhysGunDrop, "OnPhysGunDrop" ),
  
  	DEFINE_BASENPCINTERACTABLE_DATADESC(),
  
  	// DEFINE_FIELD( m_ShotSounds, FIELD_SHORT ),
  
  END_DATADESC()
  
  LINK_ENTITY_TO_CLASS( npc_turret_floor, CNPC_FloorTurret );
  
  //-----------------------------------------------------------------------------
  // Constructor
  //-----------------------------------------------------------------------------
  CNPC_FloorTurret::CNPC_FloorTurret( void ) : 
  	m_bActive( false ),
  	m_hEyeGlow( NULL ),
  	m_hLaser( NULL ),
  	m_iAmmoType( -1 ),
  	m_bAutoStart( false ),
  	m_flPingTime( 0.0f ),
  	m_flNextActivateSoundTime( 0.0f ),
  	m_bCarriedByPlayer( false ),
  	m_bUseCarryAngles( false ),
  	m_flPlayerDropTime( 0.0f ),
  	m_flShotTime( 0.0f ),
  	m_flLastSight( 0.0f ),
  	m_bBlinkState( false ),
  	m_flThrashTime( 0.0f ),
  	m_pMotionController( NULL ),
  	m_bEnabled( false ),
  	m_bSelfDestructing( false )
  {
  	m_vecGoalAngles.Init();
  
  	m_vecEnemyLKP = vec3_invalid;
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: 
  //-----------------------------------------------------------------------------
  Class_T	CNPC_FloorTurret::Classify( void ) 
  {
  	if ( m_bEnabled ) 
  	{
  		// Hacked or friendly turrets don't attack players
  		if( m_bHackedByAlyx || IsCitizenTurret() )
  			return CLASS_PLAYER_ALLY;
  
  		return CLASS_COMBINE;
  	}
  
  	return CLASS_NONE;
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: 
  //-----------------------------------------------------------------------------
  void CNPC_FloorTurret::UpdateOnRemove( void )
  {
  	if ( m_pMotionController != NULL )
  	{
  		UTIL_Remove( m_pMotionController );
  		m_pMotionController = NULL;
  	}
  
  	if ( m_hLaser != NULL )
  	{
  		UTIL_Remove( m_hLaser );
  		m_hLaser = NULL;
  	}
  
  	if ( m_hEyeGlow != NULL )
  	{
  		UTIL_Remove( m_hEyeGlow );
  		m_hEyeGlow = NULL;
  	}
  
  	BaseClass::UpdateOnRemove();
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: Precache
  //-----------------------------------------------------------------------------
  void CNPC_FloorTurret::Precache( void )
  {
  	const char *pModelName = STRING( GetModelName() );
  	pModelName = ( pModelName && pModelName[ 0 ] != '\0' ) ? pModelName : FLOOR_TURRET_MODEL;
  	PrecacheModel( pModelName );
  	PrecacheModel( FLOOR_TURRET_GLOW_SPRITE );
  
  	PropBreakablePrecacheAll( MAKE_STRING( pModelName ) );
  
  	if ( IsCitizenTurret() )
  	{
  		PrecacheModel( LASER_BEAM_SPRITE );
  		PrecacheScriptSound( "NPC_FloorTurret.AlarmPing");
  	}
  
  	// Activities
  	ADD_CUSTOM_ACTIVITY( CNPC_FloorTurret, ACT_FLOOR_TURRET_OPEN );
  	ADD_CUSTOM_ACTIVITY( CNPC_FloorTurret, ACT_FLOOR_TURRET_CLOSE );
  	ADD_CUSTOM_ACTIVITY( CNPC_FloorTurret, ACT_FLOOR_TURRET_CLOSED_IDLE );
  	ADD_CUSTOM_ACTIVITY( CNPC_FloorTurret, ACT_FLOOR_TURRET_OPEN_IDLE );
  	ADD_CUSTOM_ACTIVITY( CNPC_FloorTurret, ACT_FLOOR_TURRET_FIRE );
  	
  	PrecacheScriptSound( "NPC_FloorTurret.Retire" );
  	PrecacheScriptSound( "NPC_FloorTurret.Deploy" );
  	PrecacheScriptSound( "NPC_FloorTurret.Move" );
  	PrecacheScriptSound( "NPC_Combine.WeaponBash" );
  	PrecacheScriptSound( "NPC_FloorTurret.Activate" );
  	PrecacheScriptSound( "NPC_FloorTurret.Alert" );
  	m_ShotSounds = PrecacheScriptSound( "NPC_FloorTurret.ShotSounds" );
  	PrecacheScriptSound( "NPC_FloorTurret.Die" );
  	PrecacheScriptSound( "NPC_FloorTurret.Retract");
  	PrecacheScriptSound( "NPC_FloorTurret.Alarm");
  	PrecacheScriptSound( "NPC_FloorTurret.Ping");
  	PrecacheScriptSound( "NPC_FloorTurret.DryFire");
  	PrecacheScriptSound( "NPC_FloorTurret.Destruct" );
  
  #ifdef HL2_EPISODIC
  	PrecacheParticleSystem( "explosion_turret_break" );
  #endif // HL2_EPISODIC
  	
  	BaseClass::Precache();
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: Spawn the entity
  //-----------------------------------------------------------------------------
  void CNPC_FloorTurret::Spawn( void )
  { 
  	Precache();
  
  	const char *pModelName = STRING( GetModelName() );
  	SetModel( ( pModelName && pModelName[ 0 ] != '\0' ) ? pModelName : FLOOR_TURRET_MODEL );
  	
  	// If we're a citizen turret, we use a different skin
  	if ( IsCitizenTurret() )
  	{
  		if (m_iKeySkin == 0)
  		{	// select a "random" skin -- rather than being truly random, use a static variable
  			// to cycle through them evenly. The static won't be saved across save/load, but 
  			// frankly I don't care so much about that.
  			// m_nSkin = random->RandomInt( 1, 4 );
  			static unsigned int nextSkin = 0;
  			m_nSkin = nextSkin + 1;
  
  			// add one mod 4
  			nextSkin = (nextSkin + 1) & 0x03;
  		}
  		else
  		{	// at least make sure that it's in the right range
  			m_nSkin = clamp(m_iKeySkin,1,4);
  		}
  	}
  
  	BaseClass::Spawn();
  
  	SetBlocksLOS( false );
  
  	m_HackedGunPos	= Vector( 0, 0, 12.75 );
  	SetViewOffset( EyeOffset( ACT_IDLE ) );
  	m_flFieldOfView	= 0.4f; // 60 degrees
  	m_takedamage	= DAMAGE_EVENTS_ONLY;
  	m_iHealth		= 100;
  	m_iMaxHealth	= 100;
  
  	AddEFlags( EFL_NO_DISSOLVE );
  
  	SetPoseParameter( m_poseAim_Yaw, 0 );
  	SetPoseParameter( m_poseAim_Pitch, 0 );
  
  	m_iAmmoType = GetAmmoDef()->Index( "PISTOL" );
  
  	m_iMuzzleAttachment = LookupAttachment( "eyes" );
  	m_iEyeAttachment = LookupAttachment( "light" );
  
  	// FIXME: Do we ever need m_bAutoStart? (Sawyer)
  	m_spawnflags |= SF_FLOOR_TURRET_AUTOACTIVATE;
  
  	//Set our autostart state
  	m_bAutoStart = !!( m_spawnflags & SF_FLOOR_TURRET_AUTOACTIVATE );
  	m_bEnabled	 = ( ( m_spawnflags & SF_FLOOR_TURRET_STARTINACTIVE ) == false );
  
  	//Do we start active?
  	if ( m_bAutoStart && m_bEnabled )
  	{
  		SetThink( &CNPC_FloorTurret::AutoSearchThink );
  		SetEyeState( TURRET_EYE_DORMANT );
  	}
  	else
  	{
  		SetThink( &CNPC_FloorTurret::DisabledThink );
  		SetEyeState( TURRET_EYE_DISABLED );
  	}
  
  	// Start 
  	if ( OnSide() )
  	{
  		SetThink( &CNPC_FloorTurret::DisabledThink );
  		SetEyeState( TURRET_EYE_DISABLED );
  	}
  
  	//Stagger our starting times
  	SetNextThink( gpGlobals->curtime + random->RandomFloat( 0.1f, 0.3f ) );
  
  	SetUse( &CNPC_FloorTurret::ToggleUse );
  
  	// Don't allow us to skip animation setup because our attachments are critical to us!
  	SetBoneCacheFlags( BCF_NO_ANIMATION_SKIP );
  
  	CreateVPhysics();
  
  	SetState(NPC_STATE_IDLE);
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: 
  //-----------------------------------------------------------------------------
  void CNPC_FloorTurret::Activate( void )
  {
  	BaseClass::Activate();
  
  	// Force the eye state to the current state so that our glows are recreated after transitions
  	SetEyeState( m_iEyeState );
  
  	if ( !m_pMotionController )
  	{
  		// Create the motion controller
  		m_pMotionController = CTurretTipController::CreateTipController( this );
  
  		// Enable the controller
  		if ( m_pMotionController != NULL )
  		{
  			m_pMotionController->Enable();
  		}
  	}
  }
  
  //-----------------------------------------------------------------------------
  
  bool CNPC_FloorTurret::CreateVPhysics( void )
  {
  	//Spawn our physics hull
  	if ( VPhysicsInitNormal( SOLID_VPHYSICS, 0, false ) == NULL )
  	{
  		DevMsg( "npc_turret_floor unable to spawn physics object!\n" );
  	}
  
  	return true;
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: Retract and stop attacking
  //-----------------------------------------------------------------------------
  void CNPC_FloorTurret::Retire( void )
  {
  	if ( PreThink( TURRET_RETIRING ) )
  		return;
  
  	//Level out the turret
  	m_vecGoalAngles = GetAbsAngles();
  	SetNextThink( gpGlobals->curtime + 0.05f );
  
  	//Set ourselves to close
  	if ( GetActivity() != ACT_FLOOR_TURRET_CLOSE )
  	{
  		//Set our visible state to dormant
  		SetEyeState( TURRET_EYE_DORMANT );
  
  		SetActivity( (Activity) ACT_FLOOR_TURRET_OPEN_IDLE );
  		
  		//If we're done moving to our desired facing, close up
  		if ( UpdateFacing() == false )
  		{
  			SetActivity( (Activity) ACT_FLOOR_TURRET_CLOSE );
  			EmitSound( "NPC_FloorTurret.Retire" );
  
  			//Notify of the retraction
  			m_OnRetire.FireOutput( NULL, this );
  		}
  	}
  	else if ( IsActivityFinished() )
  	{	
  		m_bActive		= false;
  		m_flLastSight	= 0;
  
  		SetActivity( (Activity) ACT_FLOOR_TURRET_CLOSED_IDLE );
  
  		//Go back to auto searching
  		if ( m_bAutoStart )
  		{
  			SetThink( &CNPC_FloorTurret::AutoSearchThink );
  			SetNextThink( gpGlobals->curtime + 0.05f );
  		}
  		else
  		{
  			//Set our visible state to dormant
  			SetEyeState( TURRET_EYE_DISABLED );
  			SetThink( &CNPC_FloorTurret::DisabledThink );
  		}
  	}
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: Deploy and start attacking
  //-----------------------------------------------------------------------------
  void CNPC_FloorTurret::Deploy( void )
  {
  	if ( PreThink( TURRET_DEPLOYING ) )
  		return;
  
  	m_vecGoalAngles = GetAbsAngles();
  
  	SetNextThink( gpGlobals->curtime + 0.05f );
  
  	//Show we've seen a target
  	SetEyeState( TURRET_EYE_SEE_TARGET );
  
  	//Open if we're not already
  	if ( GetActivity() != ACT_FLOOR_TURRET_OPEN )
  	{
  		m_bActive = true;
  		SetActivity( (Activity) ACT_FLOOR_TURRET_OPEN );
  		EmitSound( "NPC_FloorTurret.Deploy" );
  
  		//Notify we're deploying
  		m_OnDeploy.FireOutput( NULL, this );
  	}
  
  	//If we're done, then start searching
  	if ( IsActivityFinished() )
  	{
  		SetActivity( (Activity) ACT_FLOOR_TURRET_OPEN_IDLE );
  
  		m_flShotTime  = gpGlobals->curtime + 1.0f;
  
  		m_flPlaybackRate = 0;
  		SetThink( &CNPC_FloorTurret::SearchThink );
  
  		EmitSound( "NPC_FloorTurret.Move" );
  	}
  
  	m_flLastSight = gpGlobals->curtime + FLOOR_TURRET_MAX_WAIT;	
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: 
  //-----------------------------------------------------------------------------
  void CNPC_FloorTurret::OnPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t reason )
  {
  	m_hPhysicsAttacker = pPhysGunUser;
  	m_flLastPhysicsInfluenceTime = gpGlobals->curtime;
  
  	// Drop our mass a lot so that we can be moved easily with +USE
  	if ( reason != PUNTED_BY_CANNON )
  	{
  		Assert( VPhysicsGetObject() );
  
  		m_bCarriedByPlayer = true;
  		m_OnPhysGunPickup.FireOutput( this, this );
  
  		// We want to use preferred carry angles if we're not nicely upright
  		Vector vecToTurret = pPhysGunUser->GetAbsOrigin() - GetAbsOrigin();
  		vecToTurret.z = 0;
  		VectorNormalize( vecToTurret );
  
  		// We want to use preferred carry angles if we're not nicely upright
  		Vector	forward, up;
  		GetVectors( &forward, NULL, &up );
  
  		bool bUpright = DotProduct( up, Vector(0,0,1) ) > 0.9f;
  		bool bBehind = DotProduct( vecToTurret, forward ) < 0.85f;
  
  		// Correct our angles only if we're not upright or we're mostly behind the turret
  		if ( hl2_episodic.GetBool() )
  		{
  			m_bUseCarryAngles = ( bUpright == false || bBehind );
  		}
  		else
  		{
  			m_bUseCarryAngles = ( bUpright == false );
  		}
  	}
  
  	// Clear out our last NPC to kick me, because it makes no sense now
  	m_hLastNPCToKickMe = NULL;
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: 
  //-----------------------------------------------------------------------------
  void CNPC_FloorTurret::OnPhysGunDrop( CBasePlayer *pPhysGunUser, PhysGunDrop_t Reason )
  {
  	m_hPhysicsAttacker = pPhysGunUser;
  	m_flLastPhysicsInfluenceTime = gpGlobals->curtime;
  	
  	m_bCarriedByPlayer = false;
  	m_bUseCarryAngles = false;
  	m_OnPhysGunDrop.FireOutput( this, this );
  
  	// If this is a friendly turret, remember that it was just dropped
  	if ( IRelationType( pPhysGunUser ) != D_HT )
  	{
  		m_flPlayerDropTime = gpGlobals->curtime + 2.0;
  	}
  
  	// Restore our mass to the original value
  	Assert( VPhysicsGetObject() );
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: Whether this should return carry angles
  // Output : Returns true on success, false on failure.
  //-----------------------------------------------------------------------------
  bool CNPC_FloorTurret::HasPreferredCarryAnglesForPlayer( CBasePlayer *pPlayer )
  {
  	// Don't use preferred angles on enemy turrets
  	if ( IRelationType( pPlayer ) == D_HT )
  		return false;
  
  	return m_bUseCarryAngles;
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: 
  //-----------------------------------------------------------------------------
  bool CNPC_FloorTurret::OnAttemptPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t reason )
  {
  	// Prevent players pulling enemy turrets from afar if they're in front of the turret
  	if ( reason == PICKED_UP_BY_CANNON && IRelationType( pPhysGunUser ) == D_HT )
  	{
  		Vector vecForward;
  		GetVectors( &vecForward, NULL, NULL );
  		Vector vecForce = (pPhysGunUser->GetAbsOrigin() - GetAbsOrigin());
  		float flDistance = VectorNormalize( vecForce );
  
  		// If it's over the physcannon tracelength, we're pulling it
  		if ( flDistance > physcannon_tracelength.GetFloat() )
  		{
  			float flDot = DotProduct( vecForward, vecForce );
  			if ( flDot > 0.5 )
  				return false;
  		}
  	}
  
  	return true;
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: 
  //-----------------------------------------------------------------------------
  bool CNPC_FloorTurret::HandleInteraction(int interactionType, void *data, CBaseCombatCharacter *sourceEnt)
  {
  	if ( interactionType == g_interactionCombineBash )
  	{
  		// We've been bashed by a combine soldier. Remember who it was, if we haven't got an active kicker
  		if ( !m_hLastNPCToKickMe )
  		{
  			m_hLastNPCToKickMe = sourceEnt;
  			m_flKnockOverFailedTime = gpGlobals->curtime + 3.0;
  		}
  
  		// Get knocked away
  		Vector forward, up;
  		AngleVectors( sourceEnt->GetLocalAngles(), &forward, NULL, &up );
  		ApplyAbsVelocityImpulse( forward * 100 + up * 50 );
  		CTakeDamageInfo info( sourceEnt, sourceEnt, 30, DMG_CLUB );
  		CalculateMeleeDamageForce( &info, forward, GetAbsOrigin() );
  		TakeDamage( info );
  
  		EmitSound( "NPC_Combine.WeaponBash" );
  		return true;
  	}
  
  	return BaseClass::HandleInteraction( interactionType, data, sourceEnt );
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: Returns the speed at which the turret can face a target
  //-----------------------------------------------------------------------------
  float CNPC_FloorTurret::MaxYawSpeed( void )
  {
  	//TODO: Scale by difficulty?
  	return 360.0f;
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: Return true if this turret was recently dropped by a player
  // Output : Returns true on success, false on failure.
  //-----------------------------------------------------------------------------
  bool CNPC_FloorTurret::WasJustDroppedByPlayer( void )
  {
  	if ( m_flPlayerDropTime > gpGlobals->curtime )
  		return true;
  
  	return false;
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: Causes the turret to face its desired angles
  //-----------------------------------------------------------------------------
  bool CNPC_FloorTurret::UpdateFacing( void )
  {
  	bool  bMoved = false;
  	UpdateMuzzleMatrix();
  
  	Vector vecGoalDir;
  	AngleVectors( m_vecGoalAngles, &vecGoalDir );
  
  	Vector vecGoalLocalDir;
  	VectorIRotate( vecGoalDir, m_muzzleToWorld, vecGoalLocalDir );
  
  	if ( g_debug_turret.GetBool() )
  	{
  		Vector	vecMuzzle, vecMuzzleDir;
  
  		MatrixGetColumn( m_muzzleToWorld, 3, vecMuzzle );
  		MatrixGetColumn( m_muzzleToWorld, 0, vecMuzzleDir );
  
  		NDebugOverlay::Cross3D( vecMuzzle, -Vector(2,2,2), Vector(2,2,2), 255, 255, 0, false, 0.05 );
  		NDebugOverlay::Cross3D( vecMuzzle+(vecMuzzleDir*256), -Vector(2,2,2), Vector(2,2,2), 255, 255, 0, false, 0.05 );
  		NDebugOverlay::Line( vecMuzzle, vecMuzzle+(vecMuzzleDir*256), 255, 255, 0, false, 0.05 );
  		
  		NDebugOverlay::Cross3D( vecMuzzle, -Vector(2,2,2), Vector(2,2,2), 255, 0, 0, false, 0.05 );
  		NDebugOverlay::Cross3D( vecMuzzle+(vecGoalDir*256), -Vector(2,2,2), Vector(2,2,2), 255, 0, 0, false, 0.05 );
  		NDebugOverlay::Line( vecMuzzle, vecMuzzle+(vecGoalDir*256), 255, 0, 0, false, 0.05 );
  	}
  
  	QAngle vecGoalLocalAngles;
  	VectorAngles( vecGoalLocalDir, vecGoalLocalAngles );
  
  	// Update pitch
  	float flDiff = AngleNormalize( UTIL_ApproachAngle(  vecGoalLocalAngles.x, 0.0, 0.05f * MaxYawSpeed() ) );
  	
  	SetPoseParameter( m_poseAim_Pitch, GetPoseParameter( m_poseAim_Pitch ) + ( flDiff / 1.5f ) );
  
  	if ( fabs( flDiff ) > 0.1f )
  	{
  		bMoved = true;
  	}
  
  	// Update yaw
  	flDiff = AngleNormalize( UTIL_ApproachAngle(  vecGoalLocalAngles.y, 0.0, 0.05f * MaxYawSpeed() ) );
  
  	SetPoseParameter( m_poseAim_Yaw, GetPoseParameter( m_poseAim_Yaw ) + ( flDiff / 1.5f ) );
  
  	if ( fabs( flDiff ) > 0.1f )
  	{
  		bMoved = true;
  	}
  
  	// You're going to make decisions based on this info.  So bump the bone cache after you calculate everything
  	InvalidateBoneCache();
  
  	return bMoved;
  }
  
  void CNPC_FloorTurret::DryFire( void )
  {
  	EmitSound( "NPC_FloorTurret.DryFire");
  	EmitSound( "NPC_FloorTurret.Activate" );
  
   	if ( RandomFloat( 0, 1 ) > 0.5 )
  	{
  		m_flShotTime = gpGlobals->curtime + random->RandomFloat( 1, 2.5 );
  	}
  	else
  	{
  		m_flShotTime = gpGlobals->curtime;
  	}
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: Turret will continue to fire on a target's position when it loses sight of it
  //-----------------------------------------------------------------------------
  void CNPC_FloorTurret::SuppressThink( void )
  {
  	//Allow descended classes a chance to do something before the think function
  	if ( PreThink( TURRET_SUPPRESSING ) )
  		return;
  
  	//Update our think time
  	SetNextThink( gpGlobals->curtime + 0.1f );
  
  	// Look for a new enemy
  	HackFindEnemy();
  
  	//If we've acquired an enemy, start firing at it
  	if ( !GetEnemy() )
  	{
  		SetThink( &CNPC_FloorTurret::ActiveThink );
  		return;
  	}
  
  	//See if we're done suppressing
  	if ( gpGlobals->curtime > m_flLastSight )
  	{
  		// Should we look for a new target?
  		ClearEnemyMemory();
  		SetEnemy( NULL );
  		SetThink( &CNPC_FloorTurret::SearchThink );
  		m_vecGoalAngles = GetAbsAngles();
  		
  		SpinDown();
  
  		if ( m_spawnflags & SF_FLOOR_TURRET_FASTRETIRE )
  		{
  			// Retire quickly in this case. (The case where we saw the player, but he hid again).
  			m_flLastSight = gpGlobals->curtime + FLOOR_TURRET_SHORT_WAIT;
  		}
  		else
  		{
  			m_flLastSight = gpGlobals->curtime + FLOOR_TURRET_MAX_WAIT;
  		}
  
  		return;
  	}
  
  	//Get our shot positions
  	Vector vecMid = EyePosition();
  	Vector vecMidEnemy = m_vecEnemyLKP;
  
  	//Calculate dir and dist to enemy
  	Vector	vecDirToEnemy = vecMidEnemy - vecMid;	
  
  	//We want to look at the enemy's eyes so we don't jitter
  	Vector	vecDirToEnemyEyes = vecMidEnemy - vecMid;
  	VectorNormalize( vecDirToEnemyEyes );
  
  	QAngle vecAnglesToEnemy;
  	VectorAngles( vecDirToEnemyEyes, vecAnglesToEnemy );
  
  	//Draw debug info
  	if ( g_debug_turret.GetBool() )
  	{
  		NDebugOverlay::Cross3D( vecMid, -Vector(2,2,2), Vector(2,2,2), 0, 255, 0, false, 0.05 );
  		NDebugOverlay::Cross3D( vecMidEnemy, -Vector(2,2,2), Vector(2,2,2), 0, 255, 0, false, 0.05 );
  		NDebugOverlay::Line( vecMid, vecMidEnemy, 0, 255, 0, false, 0.05f );
  	}
  
  	if ( m_flShotTime < gpGlobals->curtime && m_vecEnemyLKP != vec3_invalid )
  	{
  		Vector vecMuzzle, vecMuzzleDir;
  		UpdateMuzzleMatrix();
  		MatrixGetColumn( m_muzzleToWorld, 0, vecMuzzleDir );
  		MatrixGetColumn( m_muzzleToWorld, 3, vecMuzzle );
  
  		//Fire the gun
  		if ( DotProduct( vecDirToEnemy, vecMuzzleDir ) >= 0.9848 ) // 10 degree slop
  		{
  			if( m_spawnflags & SF_FLOOR_TURRET_OUT_OF_AMMO )
  			{
  				DryFire();
  			}
  			else
  			{
  				ResetActivity();
  				SetActivity( (Activity) ACT_FLOOR_TURRET_FIRE );
  
  				//Fire the weapon
  #if !DISABLE_SHOT
  				Shoot( vecMuzzle, vecMuzzleDir );
  #endif
  			}
  		} 
  	}
  	else
  	{
  		SetActivity( (Activity) ACT_FLOOR_TURRET_OPEN_IDLE );
  	}
  
  	//If we can see our enemy, face it
  	m_vecGoalAngles.y = vecAnglesToEnemy.y;
  	m_vecGoalAngles.x = vecAnglesToEnemy.x;
  
  	//Turn to face
  	UpdateFacing();
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: Allows the turret to fire on targets if they're visible
  //-----------------------------------------------------------------------------
  void CNPC_FloorTurret::ActiveThink( void )
  {
  	//Allow descended classes a chance to do something before the think function
  	if ( PreThink( TURRET_ACTIVE ) )
  		return;
  
  	HackFindEnemy();
  	
  	//Update our think time
  	SetNextThink( gpGlobals->curtime + 0.1f );
  
  	//If we've become inactive, go back to searching
  	if ( ( m_bActive == false ) || ( GetEnemy() == NULL ) )
  	{
  		SetEnemy( NULL );
  		m_flLastSight = gpGlobals->curtime + FLOOR_TURRET_MAX_WAIT;
  		SetThink( &CNPC_FloorTurret::SearchThink );
  		m_vecGoalAngles = GetAbsAngles();
  		return;
  	}
  	
  	//Get our shot positions
  	Vector vecMid = EyePosition();
  	Vector vecMidEnemy = GetEnemy()->BodyTarget( vecMid );
  
  	// Store off our last seen location so we can suppress it later
  	m_vecEnemyLKP = vecMidEnemy;
  
  	//Look for our current enemy
  	bool bEnemyInFOV = FInViewCone( GetEnemy() );
  	bool bEnemyVisible = FVisible( GetEnemy() ) && GetEnemy()->IsAlive();	
  
  	// Robin: This is a hack to get around the fact that the muzzle for the turret
  	// is outside it's vcollide. This means that if it leans against a thin wall, 
  	// the muzzle can be on the other side of the wall, where it's then able to see
  	// and shoot at targets. This check ensures that nothing has come between the
  	// center of the turret and the muzzle.
  	if ( bEnemyVisible )
  	{
  		trace_t tr;
  		Vector vecCenter;
  		CollisionProp()->CollisionToWorldSpace( Vector(0,0,52), &vecCenter );
  		UTIL_TraceLine( vecCenter, vecMid, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );
  		if ( tr.fraction != 1.0 )
  		{
  			bEnemyVisible = false;
  		}
  	}
  
  	//Calculate dir and dist to enemy
  	Vector	vecDirToEnemy = vecMidEnemy - vecMid;	
  	float	flDistToEnemy = VectorNormalize( vecDirToEnemy );
  
  	//Draw debug info
  	if ( g_debug_turret.GetBool() )
  	{
  		NDebugOverlay::Cross3D( vecMid, -Vector(2,2,2), Vector(2,2,2), 0, 255, 0, false, 0.05 );
  		NDebugOverlay::Cross3D( GetEnemy()->WorldSpaceCenter(), -Vector(2,2,2), Vector(2,2,2), 0, 255, 0, false, 0.05 );
  		NDebugOverlay::Line( vecMid, GetEnemy()->WorldSpaceCenter(), 0, 255, 0, false, 0.05 );
  
  		NDebugOverlay::Cross3D( vecMid, -Vector(2,2,2), Vector(2,2,2), 0, 255, 0, false, 0.05 );
  		NDebugOverlay::Cross3D( vecMidEnemy, -Vector(2,2,2), Vector(2,2,2), 0, 255, 0, false, 0.05 );
  		NDebugOverlay::Line( vecMid, vecMidEnemy, 0, 255, 0, false, 0.05f );
  	}
  
  	//See if they're past our FOV of attack
  	if ( bEnemyInFOV == false )
  	{
  		// Should we look for a new target?
  		ClearEnemyMemory();
  		SetEnemy( NULL );
  		
  		if ( m_spawnflags & SF_FLOOR_TURRET_FASTRETIRE )
  		{
  			// Retire quickly in this case. (The case where we saw the player, but he hid again).
  			m_flLastSight = gpGlobals->curtime + FLOOR_TURRET_SHORT_WAIT;
  		}
  		else
  		{
  			m_flLastSight = gpGlobals->curtime + FLOOR_TURRET_MAX_WAIT;
  		}
  
  		SetThink( &CNPC_FloorTurret::SearchThink );
  		m_vecGoalAngles = GetAbsAngles();
  		
  		SpinDown();
  
  		return;
  	}
  
  	//Current enemy is not visible
  	if ( ( bEnemyVisible == false ) || ( flDistToEnemy > FLOOR_TURRET_RANGE ))
  	{
  		m_flLastSight = gpGlobals->curtime + 2.0f;
  
  		ClearEnemyMemory();
  		SetEnemy( NULL );
  		SetThink( &CNPC_FloorTurret::SuppressThink );
  
  		return;
  	}
  
  	if ( g_debug_turret.GetBool() )
  	{
  		Vector vecMuzzle, vecMuzzleDir;
  
  		UpdateMuzzleMatrix();
  		MatrixGetColumn( m_muzzleToWorld, 0, vecMuzzleDir );
  		MatrixGetColumn( m_muzzleToWorld, 3, vecMuzzle );
  
  		// Visualize vertical firing ranges
  		for ( int i = 0; i < 4; i++ )
  		{
  			QAngle angMaxDownPitch = GetAbsAngles();
  
  			switch( i )
  			{
  			case 0:	angMaxDownPitch.x -= 15; break;
  			case 1:	angMaxDownPitch.x += 15; break;
  			case 2:	angMaxDownPitch.x -= 25; break;
  			case 3:	angMaxDownPitch.x += 25; break;
  			default:
  				break;
  			}
  
  			Vector vecMaxDownPitch;
  			AngleVectors( angMaxDownPitch, &vecMaxDownPitch );
  			NDebugOverlay::Line( vecMuzzle, vecMuzzle + (vecMaxDownPitch*256), 255, 255, 255, false, 0.1 );
  		}
  	}
  
  	if ( m_flShotTime < gpGlobals->curtime )
  	{
  		Vector vecMuzzle, vecMuzzleDir;
  
  		UpdateMuzzleMatrix();
  		MatrixGetColumn( m_muzzleToWorld, 0, vecMuzzleDir );
  		MatrixGetColumn( m_muzzleToWorld, 3, vecMuzzle );
  
  		Vector2D vecDirToEnemy2D = vecDirToEnemy.AsVector2D();
  		Vector2D vecMuzzleDir2D = vecMuzzleDir.AsVector2D();
  
  		bool bCanShoot = true;
  		float minCos3d = DOT_10DEGREE; // 10 degrees slop
  
  		if ( flDistToEnemy < 60.0 )
  		{
  			vecDirToEnemy2D.NormalizeInPlace();
  			vecMuzzleDir2D.NormalizeInPlace();
  
  			bCanShoot = ( vecDirToEnemy2D.Dot(vecMuzzleDir2D) >= DOT_10DEGREE );
  			minCos3d = 0.7071; // 45 degrees
  		}
  
  		//Fire the gun
  		if ( bCanShoot ) // 10 degree slop XY
  		{
  			float dot3d = DotProduct( vecDirToEnemy, vecMuzzleDir );
  
  			if( m_spawnflags & SF_FLOOR_TURRET_OUT_OF_AMMO )
  			{
  				DryFire();
  			}
  			else
  			{
  				if ( dot3d >= minCos3d ) 
  				{
  					ResetActivity();
  					SetActivity( (Activity) ACT_FLOOR_TURRET_FIRE );
  
  					//Fire the weapon
  #if !DISABLE_SHOT
  					Shoot( vecMuzzle, vecMuzzleDir, (dot3d < DOT_10DEGREE) );
  #endif
  				}
  			}
  		} 
  	}
  	else
  	{
  		SetActivity( (Activity) ACT_FLOOR_TURRET_OPEN_IDLE );
  	}
  
  	//If we can see our enemy, face it
  	if ( bEnemyVisible )
  	{
  		//We want to look at the enemy's eyes so we don't jitter
  		Vector	vecDirToEnemyEyes = GetEnemy()->WorldSpaceCenter() - vecMid;
  		VectorNormalize( vecDirToEnemyEyes );
  
  		QAngle vecAnglesToEnemy;
  		VectorAngles( vecDirToEnemyEyes, vecAnglesToEnemy );
  
  		m_vecGoalAngles.y = vecAnglesToEnemy.y;
  		m_vecGoalAngles.x = vecAnglesToEnemy.x;
  	}
  
  	//Turn to face
  	UpdateFacing();
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: Target doesn't exist or has eluded us, so search for one
  //-----------------------------------------------------------------------------
  void CNPC_FloorTurret::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_FLOOR_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 )
  	{
  		HackFindEnemy();
  	}
  
  	//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_FloorTurret::ActiveThink );
  		SetEyeState( TURRET_EYE_SEE_TARGET );
  
  		SpinUp();
   
  		if ( gpGlobals->curtime > m_flNextActivateSoundTime )
  		{
  			EmitSound( "NPC_FloorTurret.Activate" );
  			m_flNextActivateSoundTime = gpGlobals->curtime + 3.0;
  		}
  		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_FloorTurret::Retire );
  		return;
  	}
  	
  	//Display that we're scanning
  	m_vecGoalAngles.x = GetAbsAngles().x + ( sin( gpGlobals->curtime * 1.0f ) * 15.0f );
  	m_vecGoalAngles.y = GetAbsAngles().y + ( sin( gpGlobals->curtime * 2.0f ) * 60.0f );
  
  	//Turn and ping
  	UpdateFacing();
  	Ping();
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: Watch for a target to wander into our view
  //-----------------------------------------------------------------------------
  void CNPC_FloorTurret::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 )
  	{
  		HackFindEnemy();
  	}
  
  	//Deploy if we've got an active target
  	if ( GetEnemy() != NULL )
  	{
  		SetThink( &CNPC_FloorTurret::Deploy );
  		if ( !m_bNoAlarmSounds )
  		{
  			EmitSound( "NPC_FloorTurret.Alert" );
  		}
  	}
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: Fire!
  //-----------------------------------------------------------------------------
  void CNPC_FloorTurret::Shoot( const Vector &vecSrc, const Vector &vecDirToEnemy, bool bStrict )
  {
  	FireBulletsInfo_t info;
  
  	if ( !bStrict && GetEnemy() != NULL )
  	{
  		Vector vecDir = GetActualShootTrajectory( vecSrc );
  
  		info.m_vecSrc = vecSrc;
  		info.m_vecDirShooting = vecDir;
  		info.m_iTracerFreq = 1;
  		info.m_iShots = 1;
  		info.m_pAttacker = this;
  		info.m_vecSpread = VECTOR_CONE_PRECALCULATED;
  		info.m_flDistance = MAX_COORD_RANGE;
  		info.m_iAmmoType = m_iAmmoType;
  	}
  	else
  	{
  		info.m_vecSrc = vecSrc;
  		info.m_vecDirShooting = vecDirToEnemy;
  		info.m_iTracerFreq = 1;
  		info.m_iShots = 1;
  		info.m_pAttacker = this;
  		info.m_vecSpread = GetAttackSpread( NULL, GetEnemy() );
  		info.m_flDistance = MAX_COORD_RANGE;
  		info.m_iAmmoType = m_iAmmoType;
  	}
  
  	FireBullets( info );
  	EmitSound( "NPC_FloorTurret.ShotSounds", m_ShotSounds );
  	DoMuzzleFlash();
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: 
  // Input  : *pEnemy - 
  // Output : Returns true on success, false on failure.
  //-----------------------------------------------------------------------------
  bool CNPC_FloorTurret::IsValidEnemy( CBaseEntity *pEnemy )
  {
  	if ( m_NPCState == NPC_STATE_DEAD )
  		return false;
  
  	// Don't shoot at other turrets.
  	if ( pEnemy->m_iClassname == m_iClassname )
  		return false;
  
  	// If our eye is stuck in something, don't shoot
  	if ( UTIL_PointContents(EyePosition()) & MASK_SHOT )
  		return false;
  
  	// Turrets have limited vertical aim capability
  	//	- Can only aim +-15 degrees, + the 10 degree slop they're allowed.
  	Vector vEnemyPos = pEnemy->EyePosition();
  
  #ifdef PORTAL
  	if ( !FInViewCone( pEnemy ) || !FVisible( pEnemy ) )
  	{
  		CProp_Portal *pPortal = FInViewConeThroughPortal( pEnemy );
  
  		if ( pPortal )
  		{
  			// Translate our target across the portal
  			UTIL_Portal_PointTransform( pPortal->m_hLinkedPortal->MatrixThisToLinked(), vEnemyPos, vEnemyPos );
  		}
  	}
  #endif
  
  	Vector los = ( vEnemyPos - EyePosition() );
  
  	QAngle angleToTarget;
  	VectorAngles( los, angleToTarget );
  	float flZDiff = fabs( AngleNormalize( angleToTarget.x - GetAbsAngles().x) );
  	if ( flZDiff > 28.0f && los.LengthSqr() > 4096.0f )
  		return false;
  
  	return BaseClass::IsValidEnemy( pEnemy );
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: 
  // Input  : *pEnemy - 
  // Output : Returns true on success, false on failure.
  //-----------------------------------------------------------------------------
  bool CNPC_FloorTurret::CanBeAnEnemyOf( CBaseEntity *pEnemy )
  {
  	// If we're out of ammo, make friendly companions ignore us
  	if ( m_spawnflags & SF_FLOOR_TURRET_OUT_OF_AMMO )
  	{
  		if ( pEnemy->Classify() == CLASS_PLAYER_ALLY_VITAL )
  			return false;
  	} 
  
  	// If we're on the side, we're never anyone's enemy
  	if ( OnSide() )
  		return false;
  
  	return BaseClass::CanBeAnEnemyOf( pEnemy );
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: The turret has been tipped over and will thrash for awhile
  //-----------------------------------------------------------------------------
  void CNPC_FloorTurret::TippedThink( void )
  {
  	// Update our PVS state
  	CheckPVSCondition();
  
  	//Animate
  	StudioFrameAdvance();
  
  	SetNextThink( gpGlobals->curtime + 0.05f );
  	SetEnemy( NULL );
  
  	// If we're not on side anymore, stop thrashing
  	if ( !OnSide() )
  	{
  		ReturnToLife();
  		return;
  	}
  
  	//See if we should continue to thrash
  	if ( gpGlobals->curtime < m_flThrashTime )
  	{
  		if ( m_flShotTime < gpGlobals->curtime )
  		{
  			if( m_spawnflags & SF_FLOOR_TURRET_OUT_OF_AMMO )
  			{
  				DryFire();
  			}
  			else if ( IsCitizenTurret() == false )	// Citizen turrets don't wildly fire
  			{
  				Vector vecMuzzle, vecMuzzleDir;
  				UpdateMuzzleMatrix();
  				MatrixGetColumn( m_muzzleToWorld, 0, vecMuzzleDir );
  				MatrixGetColumn( m_muzzleToWorld, 3, vecMuzzle );
  
  				ResetActivity();
  				SetActivity( (Activity) ACT_FLOOR_TURRET_FIRE );
  
  #if !DISABLE_SHOT
  				Shoot( vecMuzzle, vecMuzzleDir );
  #endif
  			}
  
  			m_flShotTime = gpGlobals->curtime + 0.05f;
  		}
  
  		m_vecGoalAngles.x = GetAbsAngles().x + random->RandomFloat( -60, 60 );
  		m_vecGoalAngles.y = GetAbsAngles().y + random->RandomFloat( -60, 60 );
  
  		UpdateFacing();
  	}
  	else
  	{
  		//Face forward
  		m_vecGoalAngles = GetAbsAngles();
  
  		//Set ourselves to close
  		if ( GetActivity() != ACT_FLOOR_TURRET_CLOSE )
  		{
  			SetActivity( (Activity) ACT_FLOOR_TURRET_OPEN_IDLE );
  			
  			//If we're done moving to our desired facing, close up
  			if ( UpdateFacing() == false )
  			{
  				//Make any last death noises and anims
  				EmitSound( "NPC_FloorTurret.Die" );
  				SpinDown();
  
  				SetActivity( (Activity) ACT_FLOOR_TURRET_CLOSE );
  				EmitSound( "NPC_FloorTurret.Retract" );
  
  				CTakeDamageInfo	info;
  				info.SetDamage( 1 );
  				info.SetDamageType( DMG_CRUSH );
  				Event_Killed( info );
  			}
  		}
  		else if ( IsActivityFinished() )
  		{	
  			m_bActive		= false;
  			m_flLastSight	= 0;
  
  			SetActivity( (Activity) ACT_FLOOR_TURRET_CLOSED_IDLE );
  
  			// Don't need to store last NPC anymore, because I've been knocked over
  			if ( m_hLastNPCToKickMe )
  			{
  				m_hLastNPCToKickMe = NULL;
  				m_flKnockOverFailedTime = 0;
  			}
  
  			//Try to look straight
  			if ( UpdateFacing() == false )
  			{
  				m_OnTipped.FireOutput( this, this );
  				SetEyeState( TURRET_EYE_DEAD );
  				SetCollisionGroup( COLLISION_GROUP_DEBRIS_TRIGGER );
  
  				// Start thinking slowly to see if we're ever set upright somehow
  				SetThink( &CNPC_FloorTurret::InactiveThink );
  				SetNextThink( gpGlobals->curtime + 1.0f );
  			}
  		}
  	}
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: This turret is dead. See if it ever becomes upright again, and if 
  //			so, become active again.
  //-----------------------------------------------------------------------------
  void CNPC_FloorTurret::InactiveThink( void )
  {
  	// Update our PVS state
  	CheckPVSCondition();
  
  	// Wake up if we're not on our side
  	if ( !OnSide() && m_bEnabled )
  	{
  		ReturnToLife();
  		return;
  	}
  
  	if ( IsCitizenTurret() )
  	{
  		// Blink if we have ammo or our current blink is "on" and we need to turn it off again
  		if ( HasSpawnFlags( SF_FLOOR_TURRET_OUT_OF_AMMO ) == false || m_bBlinkState )
  		{
  			// If we're on our side, ping and complain to the player
  			if ( m_bBlinkState == false )
  			{
  				// Ping when the light is going to come back on
  				EmitSound( "NPC_FloorTurret.AlarmPing" );
  			}
  
  			SetEyeState( TURRET_EYE_ALARM );
  			SetNextThink( gpGlobals->curtime + 0.25f );
  		}
  	}
  	else
  	{
  		SetNextThink( gpGlobals->curtime + 1.0f );
  	}
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: 
  //-----------------------------------------------------------------------------
  void CNPC_FloorTurret::ReturnToLife( void )
  {
  	m_flThrashTime = 0;
  
  	// Enable the tip controller
  	m_pMotionController->Enable( true );
  
  	// Return to life
  	SetState( NPC_STATE_IDLE );
  	m_lifeState = LIFE_ALIVE;
  	SetCollisionGroup( COLLISION_GROUP_NONE );
  
  	// Become active again
  	Enable();
  }	
  
  //-----------------------------------------------------------------------------
  // Purpose: The turret is not doing anything at all
  //-----------------------------------------------------------------------------
  void CNPC_FloorTurret::DisabledThink( void )
  {
  	SetNextThink( gpGlobals->curtime + 0.5 );
  	if ( OnSide() )
  	{
  		m_OnTipped.FireOutput( this, this );
  		SetEyeState( TURRET_EYE_DEAD );
  		SetCollisionGroup( COLLISION_GROUP_DEBRIS );
  		SetThink( NULL );
  	}
  }
  
  //-----------------------------------------------------------------------------
  // 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_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( FLOOR_TURRET_RANGE );
  	SetEnemy( BestEnemy() );
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: Determines whether the turret is upright enough to function
  // Output : Returns true if the turret is tipped over
  //-----------------------------------------------------------------------------
  inline bool CNPC_FloorTurret::OnSide( void )
  {
  	Vector	up;
  	GetVectors( NULL, NULL, &up );
  
  	return ( DotProduct( up, Vector(0,0,1) ) < 0.5f );
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: Allows a generic think function before the others are called
  // Input  : state - which state the turret is currently in
  //-----------------------------------------------------------------------------
  bool CNPC_FloorTurret::PreThink( turretState_e state )
  {
  	// Hack to disable turrets when ai is disabled
  	if ( CAI_BaseNPC::m_nDebugBits & bits_debugDisableAI )
  	{
  		// Push our think out into the future
  		SetNextThink( gpGlobals->curtime + 0.1f );
  		return true;
  	}
   
  	CheckPVSCondition();
  
  	//Animate
  	StudioFrameAdvance();
  
  	// We're gonna blow up, so don't interrupt us
  	if ( state == TURRET_SELF_DESTRUCTING )
  		return false;
  
  	//See if we've tipped, but only do this if we're not being carried
  	if ( !IsBeingCarriedByPlayer() )
  	{
  		if ( OnSide() == false )
  		{
  			// If I still haven't fallen over after an NPC has tried to knock me down, let them know
  			if ( m_hLastNPCToKickMe && m_flKnockOverFailedTime < gpGlobals->curtime )
  			{
  				m_hLastNPCToKickMe->DispatchInteraction( g_interactionTurretStillStanding, NULL, this );
  				m_hLastNPCToKickMe = NULL;
  			}
  
  			//Debug visualization
  			if ( g_debug_turret.GetBool() )
  			{
  				Vector	up;
  				GetVectors( NULL, NULL, &up );
  
  				NDebugOverlay::Line( GetAbsOrigin()+(up*32), GetAbsOrigin()+(up*128), 0, 255, 0, false, 2.0f );
  				NDebugOverlay::Cross3D( GetAbsOrigin()+(up*32), -Vector(2,2,2), Vector(2,2,2), 0, 255, 0, false, 2.0f );
  				NDebugOverlay::Cross3D( GetAbsOrigin()+(up*128), -Vector(2,2,2), Vector(2,2,2), 0, 255, 0, false, 2.0f );
  			}
  		}
  		else
  		{
  			if ( HasSpawnFlags( SF_FLOOR_TURRET_OUT_OF_AMMO ) == false )
  			{
  				//Thrash around for a bit
  				m_flThrashTime = gpGlobals->curtime + random->RandomFloat( 2.0f, 2.5f );
  				SetNextThink( gpGlobals->curtime + 0.05f );
  
  				SetThink( &CNPC_FloorTurret::TippedThink );
  				SetEyeState( TURRET_EYE_SEE_TARGET );
  				
  				SpinUp();
  				if ( !m_bNoAlarmSounds )
  				{
  					EmitSound( "NPC_FloorTurret.Alarm" );
  				}
  			}
  			else
  			{
  				// Take away the laser
  				UTIL_Remove( m_hLaser );
  				m_hLaser = NULL;
  
  				// Become inactive
  				SetThink( &CNPC_FloorTurret::InactiveThink );
  				SetEyeState( TURRET_EYE_DEAD );
  			}
  
  			//Stop being targetted
  			SetState( NPC_STATE_DEAD );
  			m_lifeState = LIFE_DEAD;
  
  			//Disable the tip controller
  			m_pMotionController->Enable( false );
  
  			//Debug visualization
  			if ( g_debug_turret.GetBool() )
  			{
  				Vector	up;
  				GetVectors( NULL, NULL, &up );
  
  				NDebugOverlay::Line( GetAbsOrigin()+(up*32), GetAbsOrigin()+(up*128), 255, 0, 0, false, 2.0f );
  				NDebugOverlay::Cross3D( GetAbsOrigin()+(up*32), -Vector(2,2,2), Vector(2,2,2), 255, 0, 0, false, 2.0f );
  				NDebugOverlay::Cross3D( GetAbsOrigin()+(up*128), -Vector(2,2,2), Vector(2,2,2), 255, 0, 0, false, 2.0f );
  			}
  
  			//Interrupt current think function
  			return true;
  		}
  	}
  
  	//Do not interrupt current think function
  	return false;
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: Sets the state of the glowing eye attached to the turret
  // Input  : state - state the eye should be in
  //-----------------------------------------------------------------------------
  void CNPC_FloorTurret::SetEyeState( eyeState_t state )
  {
  	// Must have a valid eye to affect
  	if ( !m_hEyeGlow )
  	{
  		// Create our eye sprite
  		m_hEyeGlow = CSprite::SpriteCreate( FLOOR_TURRET_GLOW_SPRITE, GetLocalOrigin(), false );
  		if ( !m_hEyeGlow )
  			return;
  
  		m_hEyeGlow->SetTransparency( kRenderWorldGlow, 255, 0, 0, 128, kRenderFxNoDissipation );
  		m_hEyeGlow->SetAttachment( this, m_iEyeAttachment );
  	}
  
  	// Add the laser if it doesn't already exist
  	if ( IsCitizenTurret() && HasSpawnFlags( SF_FLOOR_TURRET_OUT_OF_AMMO ) == false && m_hLaser == NULL )
  	{
  		m_hLaser = CBeam::BeamCreate( LASER_BEAM_SPRITE, 1.0f );
  		if ( m_hLaser == NULL )
  			return;
  
  		m_hLaser->EntsInit( this, this );
  		m_hLaser->FollowEntity( this );
  		m_hLaser->SetStartAttachment( LookupAttachment( "laser_start" ) );
  		m_hLaser->SetEndAttachment( LookupAttachment( "laser_end" ) );
  		m_hLaser->SetNoise( 0 );
  		m_hLaser->SetColor( 255, 0, 0 );
  		m_hLaser->SetScrollRate( 0 );
  		m_hLaser->SetWidth( 1.0f );
  		m_hLaser->SetEndWidth( 1.0f );
  		m_hLaser->SetBrightness( 160 );
  		m_hLaser->SetBeamFlags( SF_BEAM_SHADEIN );
  	}
  
  	m_iEyeState = state;
  
  	//Set the state
  	switch( state )
  	{
  	default:
  	case TURRET_EYE_SEE_TARGET: //Fade in and scale up
  		m_hEyeGlow->SetColor( 255, 0, 0 );
  		m_hEyeGlow->SetBrightness( 230, 0.1f );
  		m_hEyeGlow->SetScale( 0.4f, 0.1f );
  		break;
  
  	case TURRET_EYE_SEEKING_TARGET: //Ping-pongs
  		
  		//Toggle our state
  		m_bBlinkState = !m_bBlinkState;
  		m_hEyeGlow->SetColor( 255, 128, 0 );
  
  		if ( m_bBlinkState )
  		{
  			//Fade up and scale up
  			m_hEyeGlow->SetScale( 0.25f, 0.1f );
  			m_hEyeGlow->SetBrightness( 164, 0.1f );
  		}
  		else
  		{
  			//Fade down and scale down
  			m_hEyeGlow->SetScale( 0.2f, 0.1f );
  			m_hEyeGlow->SetBrightness( 64, 0.1f );
  		}
  
  		break;
  
  	case TURRET_EYE_DORMANT: //Fade out and scale down
  		m_hEyeGlow->SetColor( 0, 255, 0 );
  		m_hEyeGlow->SetScale( 0.1f, 0.5f );
  		m_hEyeGlow->SetBrightness( 64, 0.5f );
  		break;
  
  	case TURRET_EYE_DEAD: //Fade out slowly
  		m_hEyeGlow->SetColor( 255, 0, 0 );
  		m_hEyeGlow->SetScale( 0.1f, 3.0f );
  		m_hEyeGlow->SetBrightness( 0, 3.0f );
  		break;
  
  	case TURRET_EYE_DISABLED:
  		m_hEyeGlow->SetColor( 0, 255, 0 );
  		m_hEyeGlow->SetScale( 0.1f, 1.0f );
  		m_hEyeGlow->SetBrightness( 0, 1.0f );
  		break;
  	
  	case TURRET_EYE_ALARM:
  		{
  			//Toggle our state
  			m_bBlinkState = !m_bBlinkState;
  			m_hEyeGlow->SetColor( 255, 0, 0 );
  
  			if ( m_bBlinkState )
  			{
  				//Fade up and scale up
  				m_hEyeGlow->SetScale( 0.75f, 0.05f );
  				m_hEyeGlow->SetBrightness( 192, 0.05f );
  			}
  			else
  			{
  				//Fade down and scale down
  				m_hEyeGlow->SetScale( 0.25f, 0.25f );
  				m_hEyeGlow->SetBrightness( 64, 0.25f );
  			}
  		}
  		break;
  	}
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: Make a pinging noise so the player knows where we are
  //-----------------------------------------------------------------------------
  void CNPC_FloorTurret::Ping( void )
  {
  	//See if it's time to ping again
  	if ( m_flPingTime > gpGlobals->curtime )
  		return;
  
  	//Ping!
  	EmitSound( "NPC_FloorTurret.Ping" );
  
  	SetEyeState( TURRET_EYE_SEEKING_TARGET );
  
  	m_flPingTime = gpGlobals->curtime + FLOOR_TURRET_PING_TIME;
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: Toggle the turret's state
  //-----------------------------------------------------------------------------
  void CNPC_FloorTurret::Toggle( void )
  {
  	//This turret is on its side, it can't function
  	if ( OnSide() || ( IsAlive() == false ) )
  		return;
  
  	//Toggle the state
  	if ( m_bEnabled )
  	{
  		Disable();
  	}
  	else 
  	{
  		Enable();
  	}
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: Enable the turret and deploy
  //-----------------------------------------------------------------------------
  void CNPC_FloorTurret::Enable( void )
  {
  	// Don't interrupt blowing up!
  	if ( m_bSelfDestructing )
  		return;
  
  	// Always allow us to come back to life, even if not right now
  	m_bEnabled = true;
  
  	//This turret is on its side, it can't function
  	if ( OnSide() || ( IsAlive() == false ) )
  		return;
  
  	// if the turret is flagged as an autoactivate turret, re-enable its ability open self.
  	if ( m_spawnflags & SF_FLOOR_TURRET_AUTOACTIVATE )
  	{
  		m_bAutoStart = true;
  	}
  
  	SetThink( &CNPC_FloorTurret::Deploy );
  	SetNextThink( gpGlobals->curtime + 0.05f );
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: Retire the turret until enabled again
  //-----------------------------------------------------------------------------
  void CNPC_FloorTurret::Disable( void )
  {
  	//This turret is on its side, it can't function
  	if ( OnSide() || ( IsAlive() == false ) || m_bSelfDestructing )
  		return;
  
  	if ( m_bEnabled )
  	{
  		m_bEnabled = false;
  		m_bAutoStart = false;
  
  		SetEnemy( NULL );
  		SetThink( &CNPC_FloorTurret::Retire );
  		SetNextThink( gpGlobals->curtime + 0.1f );
  	}
  	else
  		SetThink( &CNPC_FloorTurret::DisabledThink );
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: Toggle the turret's state via input function
  //-----------------------------------------------------------------------------
  void CNPC_FloorTurret::InputToggle( inputdata_t &inputdata )
  {
  	Toggle();
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: 
  //-----------------------------------------------------------------------------
  void CNPC_FloorTurret::InputEnable( inputdata_t &inputdata )
  {
  	Enable();
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: 
  //-----------------------------------------------------------------------------
  void CNPC_FloorTurret::InputDisable( inputdata_t &inputdata )
  {
  	Disable();
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: Stops the turret from firing live rounds (still attempts to though)
  //-----------------------------------------------------------------------------
  void CNPC_FloorTurret::InputDepleteAmmo( inputdata_t &inputdata )
  {
  	AddSpawnFlags( SF_FLOOR_TURRET_OUT_OF_AMMO );
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: Allows the turret to fire live rounds again
  //-----------------------------------------------------------------------------
  void CNPC_FloorTurret::InputRestoreAmmo( inputdata_t &inputdata )
  {
  	RemoveSpawnFlags( SF_FLOOR_TURRET_OUT_OF_AMMO );
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: Allow players and npc's to turn the turret on and off
  //-----------------------------------------------------------------------------
  void CNPC_FloorTurret::ToggleUse ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
  {
  	switch( useType )
  	{
  	case USE_OFF:
  		Disable();
  		break;
  	case USE_ON:
  		Enable();
  		break;
  	case USE_SET:
  		break;
  	case USE_TOGGLE:
  		Toggle( );
  		break;
  	}
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: Reduce physics forces from the front
  //-----------------------------------------------------------------------------
  int CNPC_FloorTurret::VPhysicsTakeDamage( const CTakeDamageInfo &info )
  {
  	bool bShouldIgnoreFromFront = false;
  
  	// Ignore crossbow bolts hitting us from the front
  	bShouldIgnoreFromFront = ( info.GetDamageType() & DMG_BULLET ) != 0;
  
  	// Ignore bullets from the front
  	if ( !bShouldIgnoreFromFront )
  	{
  		bShouldIgnoreFromFront = FClassnameIs( info.GetInflictor(), "crossbow_bolt" );
  	}
  
  	// Did it hit us on the front?
  	if ( bShouldIgnoreFromFront )
  	{
  		Vector vecForward;
  		GetVectors( &vecForward, NULL, NULL );
  		Vector vecForce = info.GetDamageForce();
  		VectorNormalize( vecForce );
  		float flDot = DotProduct( vecForward, vecForce );
  		if ( flDot < -0.85 )
  			return 0;
  	}
  
  	return BaseClass::VPhysicsTakeDamage( info );
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: 
  // Input  : &info - 
  //-----------------------------------------------------------------------------
  int CNPC_FloorTurret::OnTakeDamage( const CTakeDamageInfo &info )
  {
  	CTakeDamageInfo	newInfo = info;
  
  	if ( info.GetDamageType() & (DMG_SLASH|DMG_CLUB) )
  	{
  		// Take extra force from melee hits
  		newInfo.ScaleDamageForce( 2.0f );
  		
  		// Disable our upright controller for some time
  		if ( m_pMotionController != NULL )
  		{
  			m_pMotionController->Suspend( 2.0f );
  		}
  	}
  	else if ( info.GetDamageType() & DMG_BLAST )
  	{
  		newInfo.ScaleDamageForce( 2.0f );
  	}
  	else if ( (info.GetDamageType() & DMG_BULLET) && !(info.GetDamageType() & DMG_BUCKSHOT) )
  	{
  		// Bullets, but not buckshot, do extra push
  		newInfo.ScaleDamageForce( 2.5f );
  	}
  
  	// Manually apply vphysics because AI_BaseNPC takedamage doesn't call back to CBaseEntity OnTakeDamage
  	VPhysicsTakeDamage( newInfo );
  
  	// Bump up our search time
  	m_flLastSight = gpGlobals->curtime + FLOOR_TURRET_MAX_WAIT;
  
  	// Start looking around in anger if we were idle
  	if ( IsAlive() && m_bEnabled && m_bAutoStart && GetActivity() == ACT_FLOOR_TURRET_CLOSED_IDLE && m_bSelfDestructing == false )
  	{
  		SetThink( &CNPC_FloorTurret::Deploy );
  	}
  
  	return BaseClass::OnTakeDamage( newInfo );
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: 
  //-----------------------------------------------------------------------------
  void CNPC_FloorTurret::SpinUp( void )
  {
  }
  
  #define	FLOOR_TURRET_MIN_SPIN_DOWN	1.0f
  
  //-----------------------------------------------------------------------------
  // Purpose: 
  // Output : const QAngle
  //-----------------------------------------------------------------------------
  QAngle CNPC_FloorTurret::PreferredCarryAngles( void )
  {
  	// FIXME: Embed this into the class
  	static QAngle g_prefAngles;
  
  	Vector vecUserForward;
  	CBasePlayer *pPlayer = AI_GetSinglePlayer();
  	pPlayer->EyeVectors( &vecUserForward );
  
  	// If we're looking up, then face directly forward
  	if ( vecUserForward.z >= 0.0f )
  		return vec3_angle;
  
  	// Otherwise, stay "upright"
  	g_prefAngles.Init();
  	g_prefAngles.x = -pPlayer->EyeAngles().x;
  	
  	return g_prefAngles;
  }
  
  
  //-----------------------------------------------------------------------------
  // Purpose: 
  //-----------------------------------------------------------------------------
  void CNPC_FloorTurret::SpinDown( void )
  {
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: 
  // Input  : *pVictim - 
  // Output : float
  //-----------------------------------------------------------------------------
  float CNPC_FloorTurret::GetAttackDamageScale( CBaseEntity *pVictim )
  {
  	CBaseCombatCharacter *pBCC = pVictim->MyCombatCharacterPointer();
  
  	// Do extra damage to antlions & combine
  	if ( pBCC )
  	{
  		if ( pBCC->Classify() == CLASS_ANTLION )
  			return 2.0;
  			
  		if ( pBCC->Classify() == CLASS_COMBINE )
  			return 2.0;
  	}
  
  	return BaseClass::GetAttackDamageScale( pVictim );
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: 
  //-----------------------------------------------------------------------------
  Vector CNPC_FloorTurret::GetAttackSpread( CBaseCombatWeapon *pWeapon, CBaseEntity *pTarget ) 
  {
  	WeaponProficiency_t weaponProficiency = WEAPON_PROFICIENCY_AVERAGE;
  
  	// Switch our weapon proficiency based upon our target
  	if ( pTarget )
  	{
  		if ( pTarget->Classify() == CLASS_PLAYER || pTarget->Classify() == CLASS_ANTLION || pTarget->Classify() == CLASS_ZOMBIE )
  		{
  			// Make me much more accurate
  			weaponProficiency = WEAPON_PROFICIENCY_PERFECT;
  		}
  		else if ( pTarget->Classify() == CLASS_COMBINE )
  		{
  			// Make me more accurate
  			weaponProficiency = WEAPON_PROFICIENCY_VERY_GOOD;
  		}
  	}
  
  	return VECTOR_CONE_10DEGREES * ((CBaseHLCombatWeapon::GetDefaultProficiencyValues())[ weaponProficiency ].spreadscale);
  }
  
  //------------------------------------------------------------------------------
  // Do we have a physics attacker?
  //------------------------------------------------------------------------------
  CBasePlayer *CNPC_FloorTurret::HasPhysicsAttacker( float dt )
  {
  	// If the player is holding me now, or I've been recently thrown
  	// then return a pointer to that player
  	if ( IsHeldByPhyscannon( ) || (gpGlobals->curtime - dt <= m_flLastPhysicsInfluenceTime) )
  	{
  		return m_hPhysicsAttacker;
  	}
  	return NULL;
  }
  //-----------------------------------------------------------------------------
  // Purpose: Draw any debug text overlays
  // Output : Current text offset from the top
  //-----------------------------------------------------------------------------
  int CNPC_FloorTurret::DrawDebugTextOverlays( void ) 
  {
  	int text_offset = BaseClass::DrawDebugTextOverlays();
  
  	if (m_debugOverlays & OVERLAY_TEXT_BIT) 
  	{
  		if (VPhysicsGetObject())
  		{
  			char tempstr[512];
  			Q_snprintf(tempstr, sizeof(tempstr),"Mass: %.2f kg / %.2f lb (%s)", VPhysicsGetObject()->GetMass(), kg2lbs(VPhysicsGetObject()->GetMass()), GetMassEquivalent(VPhysicsGetObject()->GetMass()));
  			EntityText( text_offset, tempstr, 0);
  			text_offset++;
  		}
  	}
  
  	return text_offset;
  }
  
  void CNPC_FloorTurret::UpdateMuzzleMatrix()
  {
  	if ( gpGlobals->tickcount != m_muzzleToWorldTick )
  	{
  		m_muzzleToWorldTick = gpGlobals->tickcount;
  		GetAttachment( m_iMuzzleAttachment, m_muzzleToWorld );
  	}
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: We override this code because otherwise we start to move into the
  //			tricky realm of player avoidance.  Since we don't go through the
  //			normal NPC thinking but we ARE an NPC (...) we miss a bunch of 
  //			book keeping.  This means we can become invisible and then never
  //			reappear.
  //-----------------------------------------------------------------------------
  void CNPC_FloorTurret::PlayerPenetratingVPhysics( void )
  {
  	// We don't care!
  }
  
  #define SELF_DESTRUCT_DURATION			4.0f
  #define SELF_DESTRUCT_BEEP_MIN_DELAY	0.1f
  #define SELF_DESTRUCT_BEEP_MAX_DELAY	0.75f
  #define SELF_DESTRUCT_BEEP_MIN_PITCH	100.0f
  #define SELF_DESTRUCT_BEEP_MAX_PITCH	225.0f
  
  //-----------------------------------------------------------------------------
  // Purpose: 
  //-----------------------------------------------------------------------------
  void CNPC_FloorTurret::BreakThink( void )
  {
  	Vector vecUp;
  	GetVectors( NULL, NULL, &vecUp );
  	Vector vecOrigin = WorldSpaceCenter() + ( vecUp * 12.0f );
  
  	// Our effect
  #ifdef HL2_EPISODIC
  	DispatchParticleEffect( "explosion_turret_break", vecOrigin, GetAbsAngles() );
  #endif // HL2_EPISODIC
  
  	// K-boom
  	RadiusDamage( CTakeDamageInfo( this, this, 15.0f, DMG_BLAST ), vecOrigin, (10*12), CLASS_NONE, this );
  
  	EmitSound( "NPC_FloorTurret.Destruct" );
  
  	breakablepropparams_t params( GetAbsOrigin(), GetAbsAngles(), vec3_origin, RandomAngularImpulse( -800.0f, 800.0f ) );
  	params.impactEnergyScale = 1.0f;
  	params.defCollisionGroup = COLLISION_GROUP_INTERACTIVE;
  
  	// no damage/damage force? set a burst of 100 for some movement
  	params.defBurstScale = 100;
  	PropBreakableCreateAll( GetModelIndex(), VPhysicsGetObject(), params, this, -1, true );
  
  	// Throw out some small chunks too obscure the explosion even more
  	CPVSFilter filter( vecOrigin );
  	for ( int i = 0; i < 4; i++ )
  	{
  		Vector gibVelocity = RandomVector(-100,100);
  		int iModelIndex = modelinfo->GetModelIndex( g_PropDataSystem.GetRandomChunkModel( "MetalChunks" ) );	
  		te->BreakModel( filter, 0.0, vecOrigin, GetAbsAngles(), Vector(40,40,40), gibVelocity, iModelIndex, 150, 4, 2.5, BREAK_METAL );
  	}
  
  	// We're done!
  	UTIL_Remove( this );
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: The countdown to destruction!
  //-----------------------------------------------------------------------------
  void CNPC_FloorTurret::SelfDestructThink( void )
  {
  	// Continue to animate
  	PreThink( TURRET_SELF_DESTRUCTING );
  
  	// If we're done, explode
  	if ( ( gpGlobals->curtime - m_flDestructStartTime ) >= SELF_DESTRUCT_DURATION )
  	{
  		SetThink( &CNPC_FloorTurret::BreakThink );
  		SetNextThink( gpGlobals->curtime + 0.1f );
  		UTIL_Remove( m_hFizzleEffect );
  		m_hFizzleEffect = NULL;
  		return;
  	}
  
  	// Find out where we are in the cycle of our destruction
  	float flDestructPerc = clamp( ( gpGlobals->curtime - m_flDestructStartTime ) / SELF_DESTRUCT_DURATION, 0.0f, 1.0f );
  
  	// Figure out when our next beep should occur
  	float flBeepTime = SELF_DESTRUCT_BEEP_MAX_DELAY + ( ( SELF_DESTRUCT_BEEP_MIN_DELAY - SELF_DESTRUCT_BEEP_MAX_DELAY ) * flDestructPerc );
  
  	// If it's time to beep again, do so
  	if ( gpGlobals->curtime > ( m_flPingTime + flBeepTime ) )
  	{
  		// Figure out what our beep pitch will be
  		float flBeepPitch = SELF_DESTRUCT_BEEP_MIN_PITCH + ( ( SELF_DESTRUCT_BEEP_MAX_PITCH - SELF_DESTRUCT_BEEP_MIN_PITCH ) * flDestructPerc );
  		
  		StopSound( "NPC_FloorTurret.AlarmPing" );
  
  		// Play the beep
  		CPASAttenuationFilter filter( this, "NPC_FloorTurret.AlarmPing" );
  		EmitSound_t params;
  		params.m_pSoundName = "NPC_FloorTurret.AlarmPing";
  		params.m_nPitch = floor( flBeepPitch );
  		params.m_nFlags = SND_CHANGE_PITCH;
  		EmitSound( filter, entindex(), params );
  		
  		// Flash our eye
  		SetEyeState( TURRET_EYE_ALARM );
  		
  		// Save this as the last time we pinged
  		m_flPingTime = gpGlobals->curtime;
  		
  		// Randomly twitch
  		m_vecGoalAngles.x = GetAbsAngles().x + random->RandomFloat( -60*flDestructPerc, 60*flDestructPerc );
  		m_vecGoalAngles.y = GetAbsAngles().y + random->RandomFloat( -60*flDestructPerc, 60*flDestructPerc );
  	}
  	
  	UpdateFacing();
  
  	// Think again!
  	SetNextThink( gpGlobals->curtime + 0.05f );
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: Make us explode
  //-----------------------------------------------------------------------------
  void CNPC_FloorTurret::InputSelfDestruct( inputdata_t &inputdata )
  {
  	// Ka-boom!
  	m_flDestructStartTime = gpGlobals->curtime;
  	m_flPingTime = gpGlobals->curtime;
  	m_bSelfDestructing = true;
  
  	SetThink( &CNPC_FloorTurret::SelfDestructThink );
  	SetNextThink( gpGlobals->curtime + 0.1f );
  
  	// Create the dust effect in place
  	m_hFizzleEffect = (CParticleSystem *) CreateEntityByName( "info_particle_system" );
  	if ( m_hFizzleEffect != NULL )
  	{
  		Vector vecUp;
  		GetVectors( NULL, NULL, &vecUp );
  
  		// Setup our basic parameters
  		m_hFizzleEffect->KeyValue( "start_active", "1" );
  		m_hFizzleEffect->KeyValue( "effect_name", "explosion_turret_fizzle" );
  		m_hFizzleEffect->SetParent( this );
  		m_hFizzleEffect->SetAbsOrigin( WorldSpaceCenter() + ( vecUp * 12.0f ) );
  		DispatchSpawn( m_hFizzleEffect );
  		m_hFizzleEffect->Activate();
  	}
  }
  
  // 
  // Tip controller
  //
  
  LINK_ENTITY_TO_CLASS( floorturret_tipcontroller, CTurretTipController );
  
  
  //---------------------------------------------------------
  // Save/Restore
  //---------------------------------------------------------
  BEGIN_DATADESC( CTurretTipController )
  
  	DEFINE_FIELD( m_bEnabled,			FIELD_BOOLEAN ),
  	DEFINE_FIELD( m_flSuspendTime,	FIELD_TIME ),
  	DEFINE_FIELD( m_worldGoalAxis,	FIELD_VECTOR ),
  	DEFINE_FIELD( m_localTestAxis,	FIELD_VECTOR ),
  	DEFINE_PHYSPTR( m_pController ),
  	DEFINE_FIELD( m_angularLimit,		FIELD_FLOAT ),
  	DEFINE_FIELD( m_pParentTurret,	FIELD_CLASSPTR ),
  
  END_DATADESC()
  
  
  
  CTurretTipController::~CTurretTipController()
  {
  	if ( m_pController )
  	{
  		physenv->DestroyMotionController( m_pController );
  		m_pController = NULL;
  	}
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: 
  //-----------------------------------------------------------------------------
  void CTurretTipController::Spawn( void )
  {
  	m_bEnabled = true;
  
  	// align the object's local Z axis
  	m_localTestAxis.Init( 0, 0, 1 );
  	// with the world's Z axis
  	m_worldGoalAxis.Init( 0, 0, 1 );
  
  	// recover from up to 25 degrees / sec angular velocity
  	m_angularLimit	= 25;
  	m_flSuspendTime	= 0;
  
  	SetMoveType( MOVETYPE_NONE );
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: 
  //-----------------------------------------------------------------------------
  void CTurretTipController::Activate( void )
  {
  	BaseClass::Activate();
  
  	if ( m_pParentTurret == NULL )
  	{
  		UTIL_Remove(this);
  		return;
  	}
  
  	IPhysicsObject *pPhys = m_pParentTurret->VPhysicsGetObject();
  
  	if ( pPhys == NULL )
  	{
  		UTIL_Remove(this);
  		return;
  	}
  
  	//Setup the motion controller
  	if ( !m_pController )
  	{
  		m_pController = physenv->CreateMotionController( (IMotionEvent *)this );
  		m_pController->AttachObject( pPhys, true );
  	}
  	else
  	{
  		m_pController->SetEventHandler( this );
  	}
  }
  
  
  //-----------------------------------------------------------------------------
  // Purpose: Actual simulation for tip controller
  //-----------------------------------------------------------------------------
  IMotionEvent::simresult_e CTurretTipController::Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject, float deltaTime, Vector &linear, AngularImpulse &angular )
  {
  	if ( Enabled() == false )
  		return SIM_NOTHING;
  
  	// Don't simulate if we're being carried by the player
  	if ( m_pParentTurret->IsBeingCarriedByPlayer() )
  		return SIM_NOTHING;
  
  	float flAngularLimit = m_angularLimit;
  
  	// If we were just dropped by a friendly player, stabilise better
  	if ( m_pParentTurret->WasJustDroppedByPlayer() )
  	{
  		// Increase the controller strength a little
  		flAngularLimit += 20;
  	}
  	else
  	{
  		// If the turret has some vertical velocity, don't simulate
  		Vector vecVelocity;
  		AngularImpulse angImpulse;
  		pObject->GetVelocity( &vecVelocity, &angImpulse );
  		if ( (vecVelocity.LengthSqr() > CNPC_FloorTurret::fMaxTipControllerVelocity) || (angImpulse.LengthSqr() > CNPC_FloorTurret::fMaxTipControllerAngularVelocity) )
  			return SIM_NOTHING;
  	}
  
  	linear.Init();
  
  	AngularImpulse angVel;
  	pObject->GetVelocity( NULL, &angVel );
  
  	matrix3x4_t matrix;
  	// get the object's local to world transform
  	pObject->GetPositionMatrix( &matrix );
  
  	// Get the alignment axis in object space
  	Vector currentLocalTargetAxis;
  	VectorIRotate( m_worldGoalAxis, matrix, currentLocalTargetAxis );
  
  	float invDeltaTime = (1/deltaTime);
  	angular = ComputeRotSpeedToAlignAxes( m_localTestAxis, currentLocalTargetAxis, angVel, 1.0, invDeltaTime * invDeltaTime, flAngularLimit * invDeltaTime );
  
  	return SIM_LOCAL_ACCELERATION;
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: 
  //-----------------------------------------------------------------------------
  void CTurretTipController::Enable( bool state )
  {
  	m_bEnabled = state;
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: 
  // Input  : time - 
  //-----------------------------------------------------------------------------
  void CTurretTipController::Suspend( float time )
  {
  	m_flSuspendTime = gpGlobals->curtime + time;
  }
  
  
  float CTurretTipController::SuspendedTill( void )
  {
  	return m_flSuspendTime;
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: 
  // Output : Returns true on success, false on failure.
  //-----------------------------------------------------------------------------
  bool CTurretTipController::Enabled( void )
  {
  	if ( m_flSuspendTime > gpGlobals->curtime )
  		return false;
  
  	return m_bEnabled;
  }
  
  //-----------------------------------------------------------------------------
  //
  // Schedules
  //
  //-----------------------------------------------------------------------------
  AI_BEGIN_CUSTOM_NPC( npc_turret_floor, CNPC_FloorTurret )
  
  	DECLARE_INTERACTION( g_interactionTurretStillStanding );	
  
  AI_END_CUSTOM_NPC()
  