//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Bullseyes act as targets for other NPC's to attack and to trigger
//			events 
//
// $Workfile:     $
// $Date:         $
//
//-----------------------------------------------------------------------------
// $Log: $
//
// $NoKeywords: $
//=============================================================================//

#include	"cbase.h"
#include	"ai_default.h"
#include	"ai_task.h"
#include	"ai_schedule.h"
#include	"ai_node.h"
#include	"ai_hull.h"
#include	"ai_hint.h"
#include	"ai_memory.h"
#include	"ai_route.h"
#include	"ai_motor.h"
#include	"ai_senses.h"
#include	"soundent.h"
#include	"game.h"
#include	"npcevent.h"
#include	"entitylist.h"
#include	"activitylist.h"
#include	"animation.h"
#include	"basecombatweapon.h"
#include	"IEffects.h"
#include	"vstdlib/random.h"
#include	"engine/IEngineSound.h"
#include	"ammodef.h"
#include	"hl1_ai_basenpc.h"
#include	"studio.h"			//hitbox parsing
#include	"collisionutils.h"	//ComputeSeparatingPlane

#include	"physics_bone_follower.h"	//For BoneFollowerManager

#define ACT_T_IDLE		1010
Activity ACT_1010;
Activity ACT_1011;
Activity ACT_1012;
Activity ACT_1013;

#define ACT_T_TAP		1020
Activity ACT_1020;
Activity ACT_1021;
Activity ACT_1022;
Activity ACT_1023;

#define ACT_T_STRIKE	1030
Activity ACT_1030;
Activity ACT_1031;
Activity ACT_1032;
Activity ACT_1033;

#define ACT_T_REARIDLE	1040
Activity ACT_1040;
Activity ACT_1041;
Activity ACT_1042;
Activity ACT_1043;
Activity ACT_1044;

class CNPC_Tentacle : public CHL1BaseNPC
{
	DECLARE_CLASS( CNPC_Tentacle, CHL1BaseNPC );
public:

	CNPC_Tentacle();

	void Spawn( );
	void Precache( );
	bool KeyValue( const char *szKeyName, const char *szValue );

	bool QueryHearSound( CSound *pSound ) { return true; } // Tentacle isn't picky.

	
	int Level( float dz );
	int MyLevel( void );
	float MyHeight( void );

	// Don't allow the tentacle to go across transitions!!!
	virtual int	ObjectCaps( void ) { return CAI_BaseNPC::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }

	void Start ( void );
	void Cycle ( void );
	void HitTouch( CBaseEntity *pOther );

	void HandleAnimEvent( animevent_t *pEvent );
	float HearingSensitivity( void ) { return 2.0; };

	virtual int OnTakeDamage( const CTakeDamageInfo &info );

	bool CreateVPhysics( void );

	void UpdateOnRemove( void );

	float m_flInitialYaw;
	int m_iGoalAnim;
	int m_iLevel;
	int m_iDir;
	float m_flFramerateAdj;
	float m_flSoundYaw;
	int m_iSoundLevel;
	float m_flSoundTime;
	float m_flSoundRadius;
	int m_iHitDmg;
	float m_flHitTime;

	float m_flTapRadius;

	float m_flNextSong;
	static int g_fFlySound;
	static int g_fSquirmSound;

	float m_flMaxYaw;
	int m_iTapSound;

	Vector m_vecPrevSound;
	float m_flPrevSoundTime;

	float	MaxYawSpeed( void ) { 	return 18.0f;	}

	bool	HeardAnything( void );

	Class_T Classify ( void );
	
	CBoneFollowerManager	m_BoneFollowerManager;

	DECLARE_DATADESC();
	DEFINE_CUSTOM_AI;
};

// Crane bones that have physics followers
static const char *pTentacleFollowerBoneNames[] =
{
	"Bone08",
	"Bone09"
};

int CNPC_Tentacle::g_fFlySound;
int CNPC_Tentacle::g_fSquirmSound;

LINK_ENTITY_TO_CLASS( monster_tentacle, CNPC_Tentacle );

// stike sounds
#define TE_NONE -1
#define TE_SILO 0
#define TE_DIRT 1
#define TE_WATER 2

// animation sequence aliases 
typedef enum
{
	TENTACLE_ANIM_Pit_Idle,

	TENTACLE_ANIM_rise_to_Temp1,
	TENTACLE_ANIM_Temp1_to_Floor,
	TENTACLE_ANIM_Floor_Idle,
	TENTACLE_ANIM_Floor_Fidget_Pissed,
	TENTACLE_ANIM_Floor_Fidget_SmallRise,
	TENTACLE_ANIM_Floor_Fidget_Wave,
	TENTACLE_ANIM_Floor_Strike,
	TENTACLE_ANIM_Floor_Tap,
	TENTACLE_ANIM_Floor_Rotate,
	TENTACLE_ANIM_Floor_Rear,
	TENTACLE_ANIM_Floor_Rear_Idle,
	TENTACLE_ANIM_Floor_to_Lev1,

	TENTACLE_ANIM_Lev1_Idle,
	TENTACLE_ANIM_Lev1_Fidget_Claw,
	TENTACLE_ANIM_Lev1_Fidget_Shake,
	TENTACLE_ANIM_Lev1_Fidget_Snap,
	TENTACLE_ANIM_Lev1_Strike,
	TENTACLE_ANIM_Lev1_Tap,
	TENTACLE_ANIM_Lev1_Rotate,
	TENTACLE_ANIM_Lev1_Rear,
	TENTACLE_ANIM_Lev1_Rear_Idle,
	TENTACLE_ANIM_Lev1_to_Lev2,

	TENTACLE_ANIM_Lev2_Idle,
	TENTACLE_ANIM_Lev2_Fidget_Shake,
	TENTACLE_ANIM_Lev2_Fidget_Swing,
	TENTACLE_ANIM_Lev2_Fidget_Tut,
	TENTACLE_ANIM_Lev2_Strike,
	TENTACLE_ANIM_Lev2_Tap,
	TENTACLE_ANIM_Lev2_Rotate,
	TENTACLE_ANIM_Lev2_Rear,
	TENTACLE_ANIM_Lev2_Rear_Idle,
	TENTACLE_ANIM_Lev2_to_Lev3,

	TENTACLE_ANIM_Lev3_Idle,
	TENTACLE_ANIM_Lev3_Fidget_Shake,
	TENTACLE_ANIM_Lev3_Fidget_Side,
	TENTACLE_ANIM_Lev3_Fidget_Swipe,
	TENTACLE_ANIM_Lev3_Strike,
	TENTACLE_ANIM_Lev3_Tap,
	TENTACLE_ANIM_Lev3_Rotate,
	TENTACLE_ANIM_Lev3_Rear,
	TENTACLE_ANIM_Lev3_Rear_Idle,

	TENTACLE_ANIM_Lev1_Door_reach,

	TENTACLE_ANIM_Lev3_to_Engine,
	TENTACLE_ANIM_Engine_Idle,
	TENTACLE_ANIM_Engine_Sway,
	TENTACLE_ANIM_Engine_Swat,
	TENTACLE_ANIM_Engine_Bob,
	TENTACLE_ANIM_Engine_Death1,
	TENTACLE_ANIM_Engine_Death2,
	TENTACLE_ANIM_Engine_Death3,

	TENTACLE_ANIM_none
} TENTACLE_ANIM;

BEGIN_DATADESC( CNPC_Tentacle )
	DEFINE_FIELD( m_flInitialYaw, FIELD_FLOAT ),
	DEFINE_FIELD( m_iGoalAnim, FIELD_INTEGER ),
	DEFINE_FIELD( m_iLevel, FIELD_INTEGER ),
	DEFINE_FIELD( m_iDir, FIELD_INTEGER ),
	DEFINE_FIELD( m_flFramerateAdj, FIELD_FLOAT ),
	DEFINE_FIELD( m_flSoundYaw, FIELD_FLOAT ),
	DEFINE_FIELD( m_iSoundLevel, FIELD_INTEGER ),
	DEFINE_FIELD( m_flSoundTime, FIELD_TIME ),
	DEFINE_FIELD( m_flSoundRadius, FIELD_FLOAT ),
	DEFINE_FIELD( m_iHitDmg, FIELD_INTEGER ),
	DEFINE_FIELD( m_flHitTime, FIELD_TIME ),
	DEFINE_FIELD( m_flTapRadius, FIELD_FLOAT ),
	DEFINE_FIELD( m_flNextSong, FIELD_TIME ),
	DEFINE_FIELD( m_iTapSound, FIELD_INTEGER ),
	DEFINE_FIELD( m_flMaxYaw, FIELD_FLOAT ),
	DEFINE_FIELD( m_vecPrevSound, FIELD_POSITION_VECTOR ),
	DEFINE_FIELD( m_flPrevSoundTime, FIELD_TIME ),

	DEFINE_EMBEDDED( m_BoneFollowerManager ),

	DEFINE_THINKFUNC( Start ),
	DEFINE_THINKFUNC( Cycle ),
	DEFINE_ENTITYFUNC( HitTouch ),
END_DATADESC()

Class_T CNPC_Tentacle::Classify ( void ) 
{ 
	return CLASS_ALIEN_MONSTER;	
}


CNPC_Tentacle::CNPC_Tentacle()
{
	m_flMaxYaw = 65;
	m_iTapSound = 0;
}

bool CNPC_Tentacle::KeyValue( const char *szKeyName, const char *szValue  )
{
	if ( FStrEq( szKeyName, "sweeparc") )
	{
		 m_flMaxYaw = atof( szValue ) / 2.0;
		 return true;
	}
	else if (FStrEq( szKeyName, "sound"))
	{
		 m_iTapSound = atoi( szValue );
		 return true;
	}
	else
		return BaseClass::KeyValue( szKeyName, szValue );

	return false;
}

//
// Tentacle Spawn
//
void CNPC_Tentacle::Spawn( )
{
	Precache( );

	SetSolid( SOLID_BBOX );

	//Necessary for TestCollision to be called for hitbox ray hits
	AddSolidFlags( FSOLID_CUSTOMRAYTEST );

	SetMoveType( MOVETYPE_NONE );
	ClearEffects();
	m_iHealth			= 75;
	SetSequence( 0 );

	SetModel( "models/tentacle2.mdl" );
	UTIL_SetSize( this, Vector( -32, -32, 0 ), Vector( 32, 32, 64 ) );

	// Use our hitboxes to determine our render bounds
	CollisionProp()->SetSurroundingBoundsType( USE_HITBOXES );

	m_takedamage		= DAMAGE_AIM;
	AddFlag( FL_NPC );
	
	m_bloodColor		= BLOOD_COLOR_GREEN;

	ResetSequenceInfo( );
	m_iDir = 1;

	SetThink( &CNPC_Tentacle::Start );
	SetNextThink( gpGlobals->curtime + 0.2 );

	SetTouch( &CNPC_Tentacle::HitTouch );

	m_flInitialYaw = GetAbsAngles().y;
	GetMotor()->SetIdealYawAndUpdate( m_flInitialYaw );
	
	g_fFlySound = FALSE;
	g_fSquirmSound = FALSE;

	m_iHitDmg = 200;

	if (m_flMaxYaw <= 0)
		m_flMaxYaw = 65;

	m_NPCState = NPC_STATE_IDLE;

	UTIL_SetOrigin( this, GetAbsOrigin() );

	CreateVPhysics();

	AddEffects( EF_NOSHADOW );
}

void CNPC_Tentacle::UpdateOnRemove( void )
{
	m_BoneFollowerManager.DestroyBoneFollowers();
	BaseClass::UpdateOnRemove();
}

void CNPC_Tentacle::Precache( )
{
	PrecacheModel("models/tentacle2.mdl");

	PrecacheScriptSound( "Tentacle.Flies" );
	PrecacheScriptSound( "Tentacle.Squirm" );
	PrecacheScriptSound( "Tentacle.Sing" );
	PrecacheScriptSound( "Tentacle.HitDirt" );
	PrecacheScriptSound( "Tentacle.Swing" );
	PrecacheScriptSound( "Tentacle.Search" );
	PrecacheScriptSound( "Tentacle.Roar" );
	PrecacheScriptSound( "Tentacle.Alert" );

	BaseClass::Precache();
}


int CNPC_Tentacle::Level( float dz )
{
	if (dz < 216)
		return 0;
	if (dz < 408)
		return 1;
	if (dz < 600)
		return 2;
	return 3;
}


float CNPC_Tentacle::MyHeight( )
{
	switch ( MyLevel( ) )
	{
	case 1:
		return 256;
	case 2:
		return 448;
	case 3:
		return 640;
	}
	return 0;
}


int CNPC_Tentacle::MyLevel( )
{
	switch( GetSequence() )
	{
	case TENTACLE_ANIM_Pit_Idle: 
		return -1;

	case TENTACLE_ANIM_rise_to_Temp1:
	case TENTACLE_ANIM_Temp1_to_Floor:
	case TENTACLE_ANIM_Floor_to_Lev1:
		return 0;

	case TENTACLE_ANIM_Floor_Idle:
	case TENTACLE_ANIM_Floor_Fidget_Pissed:
	case TENTACLE_ANIM_Floor_Fidget_SmallRise:
	case TENTACLE_ANIM_Floor_Fidget_Wave:
	case TENTACLE_ANIM_Floor_Strike:
	case TENTACLE_ANIM_Floor_Tap:
	case TENTACLE_ANIM_Floor_Rotate:
	case TENTACLE_ANIM_Floor_Rear:
	case TENTACLE_ANIM_Floor_Rear_Idle:
		return 0;

	case TENTACLE_ANIM_Lev1_Idle:
	case TENTACLE_ANIM_Lev1_Fidget_Claw:
	case TENTACLE_ANIM_Lev1_Fidget_Shake:
	case TENTACLE_ANIM_Lev1_Fidget_Snap:
	case TENTACLE_ANIM_Lev1_Strike:
	case TENTACLE_ANIM_Lev1_Tap:
	case TENTACLE_ANIM_Lev1_Rotate:
	case TENTACLE_ANIM_Lev1_Rear:
	case TENTACLE_ANIM_Lev1_Rear_Idle:
		return 1;

	case TENTACLE_ANIM_Lev1_to_Lev2:
		return 1;

	case TENTACLE_ANIM_Lev2_Idle:
	case TENTACLE_ANIM_Lev2_Fidget_Shake:
	case TENTACLE_ANIM_Lev2_Fidget_Swing:
	case TENTACLE_ANIM_Lev2_Fidget_Tut:
	case TENTACLE_ANIM_Lev2_Strike:
	case TENTACLE_ANIM_Lev2_Tap:
	case TENTACLE_ANIM_Lev2_Rotate:
	case TENTACLE_ANIM_Lev2_Rear:
	case TENTACLE_ANIM_Lev2_Rear_Idle:
		return 2;

	case TENTACLE_ANIM_Lev2_to_Lev3:
		return 2;

	case TENTACLE_ANIM_Lev3_Idle:
	case TENTACLE_ANIM_Lev3_Fidget_Shake:
	case TENTACLE_ANIM_Lev3_Fidget_Side:
	case TENTACLE_ANIM_Lev3_Fidget_Swipe:
	case TENTACLE_ANIM_Lev3_Strike:
	case TENTACLE_ANIM_Lev3_Tap:
	case TENTACLE_ANIM_Lev3_Rotate:
	case TENTACLE_ANIM_Lev3_Rear:
	case TENTACLE_ANIM_Lev3_Rear_Idle:
		return 3;

	case TENTACLE_ANIM_Lev1_Door_reach:
		return -1;
	}
	return -1;
}

void CNPC_Tentacle::Start( void )
{
	SetThink( &CNPC_Tentacle::Cycle );

	CPASAttenuationFilter filter( this );

	if ( !g_fFlySound )
	{
		EmitSound( filter, entindex(), "Tentacle.Flies" );
		g_fFlySound = TRUE;
	}
	else if ( !g_fSquirmSound )
	{
		EmitSound( filter, entindex(), "Tentacle.Squirm" );
		g_fSquirmSound = TRUE;
	}
	
	SetNextThink( gpGlobals->curtime + 0.1 );
}

bool CNPC_Tentacle::HeardAnything( void )
{
	if ( HasCondition( COND_HEAR_DANGER ) || // I remove a bunch of sounds from here on purpose. Talk to me if you're changing this!(sjb)
		 HasCondition( COND_HEAR_COMBAT ) ||
		 HasCondition( COND_HEAR_WORLD )  ||
		 HasCondition( COND_HEAR_PLAYER ) )
		 return true;

	return false;
}

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;
	}
}

void CNPC_Tentacle::HandleAnimEvent( animevent_t *pEvent )
{
	switch( pEvent->event )
	{
	case 1:	// bang 
		{
			Vector vecSrc;
			QAngle angAngles;
			GetAttachment( "0", vecSrc, angAngles );

			// Vector vecSrc = GetAbsOrigin() + m_flTapRadius * Vector( cos( GetAbsAngles().y * (3.14192653 / 180.0) ), sin( GetAbsAngles().y * (M_PI / 180.0) ), 0.0 );

			// vecSrc.z += MyHeight( );

			switch( m_iTapSound )
			{
			case TE_SILO:
				UTIL_EmitAmbientSound( GetSoundSourceIndex(), vecSrc, "Tentacle.HitSilo", 1.0, SNDLVL_GUNFIRE, 0, 100);
				break;
			case TE_NONE:
				break;
			case TE_DIRT:
				UTIL_EmitAmbientSound( GetSoundSourceIndex(), vecSrc, "Tentacle.HitDirt", 1.0, SNDLVL_GUNFIRE, 0, 100);
				break;
			case TE_WATER:
				UTIL_EmitAmbientSound( GetSoundSourceIndex(), vecSrc, "Tentacle.HitWater", 1.0, SNDLVL_GUNFIRE, 0, 100);
				break;
			}
		}
		break;

	case 3: // start killing swing
		m_iHitDmg = 200;
		break;

	case 4: // end killing swing
		m_iHitDmg = 25;
		break;

	case 5: // just "whoosh" sound
		break;

	case 2:	// tap scrape
	case 6: // light tap
		{
			Vector vecSrc = GetAbsOrigin() + m_flTapRadius * Vector( cos( GetAbsAngles().y * (M_PI / 180.0) ), sin( GetAbsAngles().y * (M_PI / 180.0) ), 0.0 );

			vecSrc.z += MyHeight( );

			float flVol = random->RandomFloat( 0.3, 0.5 );

			switch( m_iTapSound )
			{
			case TE_SILO:
				UTIL_EmitAmbientSound( GetSoundSourceIndex(), vecSrc, "Tentacle.HitSilo", flVol, SNDLVL_GUNFIRE, 0, 100);
				break;
			case TE_NONE:
				break;
			case TE_DIRT:
				UTIL_EmitAmbientSound( GetSoundSourceIndex(), vecSrc, "Tentacle.HitDirt", flVol, SNDLVL_GUNFIRE, 0, 100);
				break;
			case TE_WATER:
				UTIL_EmitAmbientSound( GetSoundSourceIndex(), vecSrc, "Tentacle.HitWater", flVol, SNDLVL_GUNFIRE, 0, 100);
				break;
			}
		}
		break;


	case 7: // roar
		UTIL_EmitAmbientSound( GetSoundSourceIndex(), GetAbsOrigin() + Vector( 0, 0, MyHeight()), "Tentacle.Roar", 1.0, SNDLVL_GUNFIRE, 0, 100);
		break;

	case 8: // search
		UTIL_EmitAmbientSound( GetSoundSourceIndex(), GetAbsOrigin() + Vector( 0, 0, MyHeight()), "Tentacle.Search", 1.0, SNDLVL_GUNFIRE, 0, 100);
		break;

	case 9: // swing
		UTIL_EmitAmbientSound( GetSoundSourceIndex(), GetAbsOrigin() + Vector( 0, 0, MyHeight()), "Tentacle.Swing", 1.0, SNDLVL_GUNFIRE, 0, 100);
		break;
	default:
		BaseClass::HandleAnimEvent( pEvent );
	}
}

void CNPC_Tentacle::HitTouch( CBaseEntity *pOther )
{
	if (m_flHitTime > gpGlobals->curtime)
		return;

	// only look at the ones where the player hit me
	if( pOther == NULL || pOther->GetModelIndex() == GetModelIndex() || ( pOther->GetSolidFlags() & FSOLID_TRIGGER ) )
		 return;

	//Right now the BoneFollower will always be hit in box 0, and 
	//will pass that to us. Make *any* touch by the physics objects a kill
	//as the ragdoll only covers the top portion of the tentacle.
	if ( pOther->m_takedamage )
	{
		CTakeDamageInfo info( this, this, m_iHitDmg, DMG_CLUB );

		Vector vDamageForce = pOther->GetAbsOrigin() - GetAbsOrigin();
		VectorNormalize( vDamageForce );

		CalculateMeleeDamageForce( &info, vDamageForce, pOther->GetAbsOrigin() );
		pOther->TakeDamage( info );

		m_flHitTime = gpGlobals->curtime + 0.5;
	}
}

int CNPC_Tentacle::OnTakeDamage( const CTakeDamageInfo &info )
{
	CTakeDamageInfo i = info;

	//Don't allow the tentacle to die. Instead set health to 1, so we can do our own death and rebirth
	if( (int)i.GetDamage() >= m_iHealth )
	{
		i.SetDamage( 0.0f );
		m_iHealth = 1;
	}

	return BaseClass::OnTakeDamage( i );
}

bool CNPC_Tentacle::CreateVPhysics( void )
{
	BaseClass::CreateVPhysics();

	IPhysicsObject *pPhysics = VPhysicsGetObject();
	if( pPhysics )
	{
		unsigned short flags = pPhysics->GetCallbackFlags();

		flags |= CALLBACK_GLOBAL_TOUCH;

		pPhysics->SetCallbackFlags( flags );
	}
	m_BoneFollowerManager.InitBoneFollowers( this, ARRAYSIZE(pTentacleFollowerBoneNames), pTentacleFollowerBoneNames );

	return true;
}

//------------------------------------------------------------------------------
//
// Schedules
//
//------------------------------------------------------------------------------

AI_BEGIN_CUSTOM_NPC( monster_tentacle, CNPC_Tentacle )

	DECLARE_ACTIVITY( ACT_1010 )
	DECLARE_ACTIVITY( ACT_1011 )
	DECLARE_ACTIVITY( ACT_1012 )
	DECLARE_ACTIVITY( ACT_1013 )

	DECLARE_ACTIVITY( ACT_1020 )
	DECLARE_ACTIVITY( ACT_1021 )
	DECLARE_ACTIVITY( ACT_1022 )
	DECLARE_ACTIVITY( ACT_1023 )

	DECLARE_ACTIVITY( ACT_1030 )
	DECLARE_ACTIVITY( ACT_1031 )
	DECLARE_ACTIVITY( ACT_1032 )
	DECLARE_ACTIVITY( ACT_1033 )

	DECLARE_ACTIVITY( ACT_1040 )
	DECLARE_ACTIVITY( ACT_1041 )
	DECLARE_ACTIVITY( ACT_1042 )
	DECLARE_ACTIVITY( ACT_1043 )
	DECLARE_ACTIVITY( ACT_1044 )

AI_END_CUSTOM_NPC()