//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:		barnacle - stationary ceiling mounted 'fishing' monster	
//
// $Workfile:     $
// $Date:         $
// $NoKeywords: $
//=============================================================================//

#include "cbase.h"
#include "physics_prop_ragdoll.h"
#include "npc_barnacle.h"
#include "npcevent.h"
#include "gib.h"
#include "ai_default.h"
#include "activitylist.h"
#include "hl2_player.h"
#include "vstdlib/random.h"
#include "physics_saverestore.h"
#include "vcollide_parse.h"
#include "vphysics/constraints.h"
#include "studio.h"
#include "bone_setup.h"
#include "iservervehicle.h"
#include "collisionutils.h"
#include "combine_mine.h"
#include "explode.h"
#include "npc_BaseZombie.h"
#include "modelentities.h"

#if HL2_EPISODIC
#include "npc_antlion.h"
#endif

// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"

extern ConVar	sv_gravity;
ConVar	sk_barnacle_health( "sk_barnacle_health","0");

static ConVar npc_barnacle_swallow( "npc_barnacle_swallow", "0", 0, "Use prototype swallow code." );

const char *CNPC_Barnacle::m_szGibNames[NUM_BARNACLE_GIBS] =
{
	"models/gibs/hgibs.mdl",
	"models/gibs/hgibs_scapula.mdl",
	"models/gibs/hgibs_rib.mdl",
	"models/gibs/hgibs_spine.mdl"
};

//-----------------------------------------------------------------------------
// Private activities.
//-----------------------------------------------------------------------------
int ACT_BARNACLE_SLURP;			// Pulling the tongue up with prey on the end
int ACT_BARNACLE_BITE_HUMAN;	// Biting the head of a humanoid
int ACT_BARNACLE_BITE_PLAYER;	// Biting the head of the player
int ACT_BARNACLE_CHEW_HUMAN;	// Slowly swallowing the humanoid
int ACT_BARNACLE_BARF_HUMAN;	// Spitting out human legs & gibs
int ACT_BARNACLE_TONGUE_WRAP;	// Wrapping the tongue around a target
int ACT_BARNACLE_TASTE_SPIT;	// Yuck! Me no like that!
int ACT_BARNACLE_BITE_SMALL_THINGS;	// Eats small things
int ACT_BARNACLE_CHEW_SMALL_THINGS;	// Chews small things


//-----------------------------------------------------------------------------
// Interactions
//-----------------------------------------------------------------------------
int	g_interactionBarnacleVictimDangle	= 0;
int	g_interactionBarnacleVictimReleased	= 0;
int	g_interactionBarnacleVictimGrab		= 0;
int g_interactionBarnacleVictimBite     = 0;

LINK_ENTITY_TO_CLASS( npc_barnacle, CNPC_Barnacle );

// Tongue Spring constants
#define BARNACLE_TONGUE_SPRING_CONSTANT_HANGING			10000
#define BARNACLE_TONGUE_SPRING_CONSTANT_LIFTING			10000
#define BARNACLE_TONGUE_SPRING_CONSTANT_LOWERING		7000
#define BARNACLE_TONGUE_SPRING_DAMPING					20
#define BARNACLE_TONGUE_TIP_MASS						100
#define BARNACLE_TONGUE_MAX_LIFT_MASS					70

#define BARNACLE_BITE_DAMAGE_TO_PLAYER					15
#define BARNACLE_DEAD_TONGUE_ALTITUDE					164
#define BARNACLE_MIN_DEAD_TONGUE_CLEARANCE				78


//=========================================================
// Monster's Anim Events Go Here
//=========================================================
#define	BARNACLE_AE_PUKEGIB	2
#define	BARNACLE_AE_BITE	3
#define	BARNACLE_AE_SPIT	4

int AE_BARNACLE_PUKEGIB;
int AE_BARNACLE_BITE;
int AE_BARNACLE_SPIT;

#if BARNACLE_USE_TONGUE_OFFSET
// Static variable that holds the difference between the player's
// eyepos and the tongue when he is seized -- used for offsetting
// the drawing of the tongue so that it doesn't appear to clip into
// the camera when we recenter the player.
const Vector CNPC_Barnacle::m_svPlayerHeldTipOffset(24,0,-8);
#endif

//-----------------------------------------------------------------------------
// Purpose: Constructor
// Input  :
// Output :
//-----------------------------------------------------------------------------	
CNPC_Barnacle::CNPC_Barnacle(void)
{
	m_flRestUnitsAboveGround = 16.0f;
	m_flNextBloodTime = -1.0f;
#ifndef _XBOX
	m_nBloodColor = BLOOD_COLOR_YELLOW;
#endif
	m_bPlayerWasStanding = false;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
CNPC_Barnacle::~CNPC_Barnacle( void )
{
	// Destroy the ragdoll->tongue tip constraint
  	if ( m_pConstraint )
  	{
  		physenv->DestroyConstraint( m_pConstraint );
  		m_pConstraint = NULL;
  	}
}

/*

	input LetGo(void) : "Let go of anything I am holding."
	
	output OnGrab(string)    : "When I attach my tongue to something"
	output OnRelease(string) : "When I let go of something" 
*/

BEGIN_DATADESC( CNPC_Barnacle )

	DEFINE_FIELD( m_flAltitude, FIELD_FLOAT ),
	DEFINE_FIELD( m_cGibs, FIELD_INTEGER ),// barnacle loads up on gibs each time it kills something.
	DEFINE_FIELD( m_bLiftingPrey, FIELD_BOOLEAN ),
	DEFINE_FIELD( m_bSwallowingPrey, FIELD_BOOLEAN ),
	DEFINE_FIELD( m_flDigestFinish, FIELD_TIME ),
	DEFINE_FIELD( m_bPlayedPullSound, FIELD_BOOLEAN ),
	DEFINE_FIELD( m_bPlayerWasStanding, FIELD_BOOLEAN ),
	DEFINE_FIELD( m_flVictimHeight, FIELD_FLOAT ),
	DEFINE_FIELD( m_iGrabbedBoneIndex, FIELD_INTEGER ),

	DEFINE_FIELD( m_vecRoot, FIELD_POSITION_VECTOR ),
	DEFINE_FIELD( m_vecTip, FIELD_POSITION_VECTOR ),
	DEFINE_FIELD( m_hTongueRoot, FIELD_EHANDLE ),
	DEFINE_FIELD( m_hTongueTip, FIELD_EHANDLE ),
	DEFINE_FIELD( m_hRagdoll, FIELD_EHANDLE ),
	DEFINE_AUTO_ARRAY( m_pRagdollBones, FIELD_MATRIX3X4_WORLDSPACE ),
	DEFINE_PHYSPTR( m_pConstraint ),
	DEFINE_KEYFIELD( m_flRestUnitsAboveGround, FIELD_FLOAT, "RestDist" ),
	DEFINE_FIELD( m_nSpitAttachment, FIELD_INTEGER ),
	DEFINE_FIELD( m_hLastSpitEnemy, FIELD_EHANDLE ),
	DEFINE_FIELD( m_nShakeCount, FIELD_INTEGER ),
	DEFINE_FIELD( m_flNextBloodTime, FIELD_TIME ),
#ifndef _XBOX
	DEFINE_FIELD( m_nBloodColor, FIELD_INTEGER ),
#endif
	DEFINE_FIELD( m_vecBloodPos, FIELD_POSITION_VECTOR ),
	DEFINE_FIELD( m_flBarnaclePullSpeed, FIELD_FLOAT ),
	DEFINE_FIELD( m_flLocalTimer, FIELD_TIME ),
	DEFINE_FIELD( m_vLastEnemyPos, FIELD_POSITION_VECTOR ),
	DEFINE_FIELD( m_flLastPull, FIELD_FLOAT ),
	DEFINE_EMBEDDED( m_StuckTimer ),

	DEFINE_INPUTFUNC( FIELD_VOID, "DropTongue", InputDropTongue ),
	DEFINE_INPUTFUNC( FIELD_INTEGER, "SetDropTongueSpeed", InputSetDropTongueSpeed ),

#ifdef HL2_EPISODIC
	DEFINE_INPUTFUNC( FIELD_VOID, "LetGo", InputLetGo ),
	DEFINE_OUTPUT( m_OnGrab,     "OnGrab" ),
	DEFINE_OUTPUT( m_OnRelease, "OnRelease" ),
#endif

	// Function pointers
	DEFINE_THINKFUNC( BarnacleThink ),
	DEFINE_THINKFUNC( WaitTillDead ),

	DEFINE_FIELD( m_bSwallowingBomb, FIELD_BOOLEAN ),

END_DATADESC()

IMPLEMENT_SERVERCLASS_ST( CNPC_Barnacle, DT_Barnacle )
	SendPropFloat(  SENDINFO( m_flAltitude ), 0, SPROP_NOSCALE),
	SendPropVector( SENDINFO( m_vecRoot ), 0, SPROP_COORD ),
	SendPropVector( SENDINFO( m_vecTip ), 0, SPROP_COORD ), 
	SendPropVector( SENDINFO( m_vecTipDrawOffset ), 0, SPROP_NOSCALE ), 
END_SEND_TABLE()


//=========================================================
// Classify - indicates this monster's place in the 
// relationship table.
//=========================================================
Class_T	CNPC_Barnacle::Classify ( void )
{
	return	CLASS_BARNACLE;
}

//-----------------------------------------------------------------------------
// Purpose: Initialize absmin & absmax to the appropriate box
//-----------------------------------------------------------------------------
void CNPC_Barnacle::ComputeWorldSpaceSurroundingBox( Vector *pVecWorldMins, Vector *pVecWorldMaxs )
{
	// Extend our bounding box downwards the length of the tongue
	CollisionProp()->WorldSpaceAABB( pVecWorldMins, pVecWorldMaxs );

	// We really care about the tongue tip. The altitude is not really relevant.
	VectorMin( *pVecWorldMins, m_vecTip, *pVecWorldMins );
	VectorMax( *pVecWorldMaxs, m_vecTip, *pVecWorldMaxs );

//	pVecWorldMins->z -= m_flAltitude;
}

//=========================================================
// HandleAnimEvent - catches the monster-specific messages
// that occur when tagged animation frames are played.
//
// Returns number of events handled, 0 if none.
//=========================================================
void CNPC_Barnacle::HandleAnimEvent( animevent_t *pEvent )
{
	if ( pEvent->event== AE_BARNACLE_PUKEGIB )
	{
		CGib::SpawnSpecificGibs( this, 1, 50, 1, "models/gibs/hgibs_rib.mdl");
		return;
	}
	if ( pEvent->event == AE_BARNACLE_BITE )
	{
		BitePrey();
		return;
	}
	if ( pEvent->event == AE_BARNACLE_SPIT )
	{
		SpitPrey();
		return;
	}
	
	BaseClass::HandleAnimEvent( pEvent );
}

//=========================================================
// Spawn
//=========================================================
void CNPC_Barnacle::Spawn()
{
	Precache( );

	SetModel( "models/barnacle.mdl" );
	UTIL_SetSize( this, Vector(-16, -16, -40), Vector(16, 16, 0) );

	SetSolid( SOLID_BBOX );
	AddSolidFlags( FSOLID_NOT_STANDABLE );
	CollisionProp()->SetSurroundingBoundsType( USE_GAME_CODE );
#if HL2_EPISODIC // the episodic barnacle is solid, so it can be sawbladed.
	SetMoveType( MOVETYPE_PUSH );
#else
	SetMoveType( MOVETYPE_NONE );
#endif
	SetBloodColor( BLOOD_COLOR_GREEN );
	m_iHealth			= sk_barnacle_health.GetFloat();
	m_flFieldOfView		= 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result )
	m_NPCState			= NPC_STATE_NONE;
	m_cGibs				= 0;
	m_bLiftingPrey		= false;
	m_bSwallowingPrey	= false;
	m_bSwallowingBomb	= false;
	m_flDigestFinish	= 0;
	m_takedamage		= DAMAGE_YES;
	m_pConstraint		= NULL;
	m_nShakeCount = 0;
#if HL2_EPISODIC // the episodic barnacle is solid, so it can be sawbladed.
	IPhysicsObject *pPhys = VPhysicsInitShadow( false, false );
	if (pPhys)
	{
		pPhys->SetMass(500);
	}
#endif
	InitBoneControllers();
	InitTonguePosition();

	// set eye position
	SetDefaultEyeOffset();


	// Add some variation because we're often in large bunches
	SetActivity( ACT_IDLE );
	SetPlaybackRate( random->RandomFloat( 0.8f, 1.2f ) );

	SetThink ( &CNPC_Barnacle::BarnacleThink );
	SetNextThink( gpGlobals->curtime + 0.5f );

	m_flBarnaclePullSpeed = BARNACLE_PULL_SPEED;

	//Do not have a shadow
	AddEffects( EF_NOSHADOW );

	AddFlag( FL_AIMTARGET );
}


//-----------------------------------------------------------------------------
// Sets the tongue's height
//-----------------------------------------------------------------------------
void CNPC_Barnacle::SetAltitude( float flAltitude )
{
	if ( HasSpawnFlags( SF_BARNACLE_AMBUSH ) )
		return;

	m_flAltitude = flAltitude;
}

void CNPC_Barnacle::DropTongue( void )
{
	if ( m_hTongueRoot )
		return;

	m_hTongueRoot = CBarnacleTongueTip::CreateTongueRoot( m_vecRoot, QAngle(90,0,0) );
	m_hTongueTip = CBarnacleTongueTip::CreateTongueTip( this, m_hTongueRoot, m_vecTip, QAngle(0,0,0) );
	m_nSpitAttachment = LookupAttachment( "StrikeHeadAttach" );
	Assert( m_hTongueRoot && m_hTongueTip );

	RemoveSpawnFlags( SF_BARNACLE_AMBUSH );
}
//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CNPC_Barnacle::Activate( void )
{
	BaseClass::Activate();

	if ( HasSpawnFlags( SF_BARNACLE_AMBUSH ) )
		return;

	// Create our tongue tips
	if ( !m_hTongueRoot )
	{
		DropTongue();
	}
	else if ( GetEnemy() && IsEnemyAPlayer() && !m_pConstraint )
	{
		IPhysicsObject *pPlayerPhys = GetEnemy()->VPhysicsGetObject();
		IPhysicsObject *pTonguePhys = m_hTongueTip->VPhysicsGetObject();

		constraint_fixedparams_t fixed;
		fixed.Defaults();
		fixed.InitWithCurrentObjectState( pTonguePhys, pPlayerPhys );
		fixed.constraint.Defaults();
		m_pConstraint = physenv->CreateFixedConstraint( pTonguePhys, pPlayerPhys, NULL, fixed );
	}
}

//-----------------------------------------------------------------------------
// Purpose:
// Input  :
// Output :
//-----------------------------------------------------------------------------
int	CNPC_Barnacle::OnTakeDamage_Alive( const CTakeDamageInfo &inputInfo )
{
	CTakeDamageInfo info = inputInfo;
	if ( info.GetDamageType() & DMG_CLUB )
	{
		info.SetDamage( m_iHealth );
	}

	if ( GetActivity() == ACT_IDLE )
	{
		SetActivity( ACT_SMALL_FLINCH );
	}

	if( hl2_episodic.GetBool() && info.GetAttacker() && info.GetAttacker()->Classify() == CLASS_PLAYER_ALLY_VITAL )
	{
		if( FClassnameIs( info.GetAttacker(), "npc_alyx" ) )
		{
			// Alyx does double damage to barnacles, so that she can save the 
			// player's life in a more timely fashion. (sjb)
			info.ScaleDamage( 2.0f );
		}
	}

	DropTongue();

	return BaseClass::OnTakeDamage_Alive( info );
}

//-----------------------------------------------------------------------------
// Purpose: Player has illuminated this NPC with the flashlight
//-----------------------------------------------------------------------------
void CNPC_Barnacle::PlayerHasIlluminatedNPC( CBasePlayer *pPlayer, float flDot )
{
	// Create a sound to scare friendly allies away from the base on the barnacle
	if( IsAlive() )
	{
 		CSoundEnt::InsertSound( SOUND_MOVE_AWAY | SOUND_CONTEXT_ALLIES_ONLY, m_vecTip, 60, FLASHLIGHT_NPC_CHECK_INTERVAL );
	}
}

//-----------------------------------------------------------------------------
// Purpose: Initialize tongue position when first spawned
// Input  :
// Output :
//-----------------------------------------------------------------------------
void CNPC_Barnacle::InitTonguePosition( void )
{
	CBaseEntity *pTouchEnt;
	float flLength;

	pTouchEnt = TongueTouchEnt( &flLength );
	SetAltitude( flLength );

	Vector origin;

	GetAttachment( "TongueEnd", origin );

	float flTongueAdj = origin.z - GetAbsOrigin().z;
	m_vecRoot = origin - Vector(0,0,flTongueAdj);
	m_vecTip.Set( m_vecRoot.Get() - Vector(0,0,(float)m_flAltitude) );
	CollisionProp()->MarkSurroundingBoundsDirty();
}

//-----------------------------------------------------------------------------
// Purpose: 
// TODO: The LostPrey(true) at the top of 	if ( m_hRagdoll ) isnt' quite right:
// it will make the barnacle drop anything that's shot on the way up. This is a 
// quick fix for the antlions which crashed otherwise (they have somewhat anomalous
// ragdoll behaivor) but should be revisted.
//-----------------------------------------------------------------------------
void CNPC_Barnacle::BarnacleThink ( void )
{
	CBaseEntity *pTouchEnt;
	float flLength;

 	SetNextThink( gpGlobals->curtime + 0.1f );

	UpdateTongue();

	// AI Disabled, don't do anything?
	if ( CAI_BaseNPC::m_nDebugBits & bits_debugDisableAI )
		return;
	
	// Do we have an enemy?
	if ( m_hRagdoll )
	{
		if ( m_bLiftingPrey )
		{	
			if ( GetEnemy() )
			{
				LiftPrey();
			}
			else
			{
				LostPrey(true);
			}
		}
		else if ( m_bSwallowingPrey )
		{
			// Slowly swallowing the ragdoll
			SwallowPrey();
		}
		// Stay bloated as we digest
		else if ( m_flDigestFinish )
		{
			// Still digesting him>
			if ( m_flDigestFinish > gpGlobals->curtime )
			{
				if ( IsActivityFinished() )
				{
					SetActivity( ACT_IDLE );
				}

				// bite prey every once in a while
				if ( random->RandomInt(0,25) == 0 )
				{
					EmitSound( "NPC_Barnacle.Digest" );
				}
			}
			else
			{
				// Finished digesting
#if HL2_EPISODIC
				// have to save this off because LostPrey() resets it (and if we take damage before hitting that,
				// then the dead thing will go flying)
				bool poisoned = m_bSwallowingPoison;

				LostPrey( true ); // Remove all evidence
				m_flDigestFinish = 0;

				if ( poisoned )
				{	// hurt me
					TakeDamage( CTakeDamageInfo( this, this, m_iHealth, DMG_ACID ) );
				}
#else
				LostPrey( true ); // Remove all evidence
				m_flDigestFinish = 0;
#endif
			}
		}
	}
	else if ( GetEnemy()  )
	{
 		if ( m_bLiftingPrey || m_bSwallowingBomb == true )
		{	
			LiftPrey();
		}
		// Stay bloated as we digest
		else if ( m_flDigestFinish )
		{
			// Still digesting him
			if ( m_flDigestFinish > gpGlobals->curtime )
			{
				if ( IsActivityFinished() )
				{
					SetActivity( ACT_IDLE );
				}

				// bite prey every once in a while
				if ( random->RandomInt(0,25) == 0 )
				{
					EmitSound( "NPC_Barnacle.Digest" );
				}
			}
			else
			{
				// Finished digesting
#if HL2_EPISODIC
				// have to save this off because LostPrey() resets it (and if we take damage before hitting that,
				// then the dead thing will go flying)
				bool poisoned = m_bSwallowingPoison;

				LostPrey( true ); // Remove all evidence
				m_flDigestFinish = 0;

				if ( poisoned )
				{	// hurt me
					TakeDamage( CTakeDamageInfo( this, this, m_iHealth, DMG_ACID ) );
				}
#else
				LostPrey( true ); // Remove all evidence
				m_flDigestFinish = 0;
#endif
			}
		}
	}
	else
	{
		// Were we lifting prey?
		if ( m_bSwallowingPrey || m_bLiftingPrey  ) 
		{
			// Something removed our prey.
			LostPrey( false );
		}

		// barnacle has no prey right now, so just idle and check to see if anything is touching the tongue.

		// If idle and no nearby client, don't think so often
		// NOTE: Use the surrounding bounds so that we'll think often event if the tongue
		// tip is in the PVS but the body isn't
		Vector vecSurroundMins, vecSurroundMaxs;
		CollisionProp()->WorldSpaceSurroundingBounds( &vecSurroundMins, &vecSurroundMaxs );
		if ( !UTIL_FindClientInPVS( vecSurroundMins, vecSurroundMaxs ) )
		{
			SetNextThink( gpGlobals->curtime + random->RandomFloat(1,1.5) );	// Stagger a bit to keep barnacles from thinking on the same frame
		}

		if ( IsActivityFinished() && GetActivity() != ACT_IDLE )
		{
			// this is done so barnacle will fidget.

			// Add some variation because we're often in large bunches
			SetActivity( ACT_IDLE );
			SetPlaybackRate( random->RandomFloat( 0.8f, 1.2f ) );
		}

		if ( m_cGibs && random->RandomInt(0,99) == 1 )
		{
			// cough up a gib.
			CGib::SpawnSpecificGibs( this, 1, 50, 1, "models/gibs/hgibs_rib.mdl");
			m_cGibs--;

			EmitSound( "NPC_Barnacle.Digest" );
		}

		pTouchEnt = TongueTouchEnt( &flLength );

		// If there's something under us, lower the tongue down so we can grab it
		if ( m_flAltitude < flLength )
		{
			float dt = gpGlobals->curtime - GetLastThink();
			SetAltitude( m_flAltitude + m_flBarnaclePullSpeed * dt );
		}

		// NOTE: SetAltitude above will change m_flAltitude, hence the second check
		if ( m_flAltitude >= flLength )
		{
			// If we're already low enough, try to grab.
			bool bGrabbedTarget = false;
			if ( ( pTouchEnt != NULL ) && ( pTouchEnt != m_hLastSpitEnemy.Get() ) )
			{
				// tongue is fully extended, and is touching someone.
				CBaseCombatCharacter *pBCC = dynamic_cast<CBaseCombatCharacter *>(pTouchEnt);

				if( CanPickup( pBCC ) )
				{
					Vector vecGrabPos = pTouchEnt->EyePosition();
					if( !pBCC || pBCC->DispatchInteraction( g_interactionBarnacleVictimGrab, &vecGrabPos, this ) )
					{
						EmitSound( "NPC_Barnacle.BreakNeck" );
						AttachTongueToTarget( pTouchEnt, vecGrabPos );
						
						// Set the local timer to 60 seconds, which starts the lifting phase on
						// the upshot of the sine wave which right away makes it more obvious
						// that the player is being lifted.
						m_flLocalTimer = 60.0f;
						m_vLastEnemyPos = pTouchEnt->GetAbsOrigin();
						m_flLastPull = 0;
						m_StuckTimer.Set(3.0);
						bGrabbedTarget = true;

						// Set our touch flag so no one else tries to grab us this frame
						pTouchEnt->AddEFlags( EFL_IS_BEING_LIFTED_BY_BARNACLE );
					}
				}
			}

			if ( !bGrabbedTarget )
			{
				// Restore the hanging spring constant 
				if ( m_hTongueTip )
				{
					m_hTongueTip->m_pSpring->SetSpringConstant( BARNACLE_TONGUE_SPRING_CONSTANT_HANGING );
				}
				SetAltitude( flLength );
			}
		}
	}

	// NDebugOverlay::Box( GetAbsOrigin() - Vector( 0, 0, m_flAltitude ), Vector( -2, -2, -2 ), Vector( 2, 2, 2 ), 255,255,255, 0, 0.1 );

	StudioFrameAdvance();
	DispatchAnimEvents( this );
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CNPC_Barnacle::CanPickup( CBaseCombatCharacter *pBCC )
{
	// Barnacle can pick this item up because it has already passed the filters
	// in TongueTouchEnt. It just isn't an NPC or player and doesn't need further inspection.
	if( !pBCC )
		return true;

	// Don't pickup turrets
	if( FClassnameIs( pBCC, "npc_turret_floor" ) )
		return false;

	// Don't pick up a dead player or NPC
	if( !pBCC->IsAlive() )
		return false;

	if( pBCC->IsPlayer() )
	{
		CBasePlayer *pPlayer = dynamic_cast<CBasePlayer*>(pBCC);
	
		Assert( pPlayer != NULL );

		// Don't pick up a player held by another barnacle
		if( pPlayer->HasPhysicsFlag(PFLAG_ONBARNACLE) )
			return false;
	}
	else if ( pBCC->IsInAVehicle() )
	{
		// Don't pluck an NPC from a vehicle.
		return false;
	}

	return true;
}

//-----------------------------------------------------------------------------
// Allows the ragdoll to settle before biting it
//-----------------------------------------------------------------------------
bool CNPC_Barnacle::WaitForRagdollToSettle( float flBiteZOffset )
{
	Vector vecVictimPos = GetEnemy()->GetAbsOrigin();

	Vector vecCheckPos;
	QAngle vecBoneAngles;
	m_hRagdoll->GetBonePosition( m_iGrabbedBoneIndex, vecCheckPos, vecBoneAngles );

	// Stop sucking while we wait for the ragdoll to settle
	SetActivity( ACT_IDLE );

	Vector vecVelocity;
	AngularImpulse angVel;
	float flDelta = 4.0;

	// Only bite if the target bone is in the right position.
	Vector vecBitePoint = GetAbsOrigin();
	vecBitePoint.z -= flBiteZOffset;

   	//NDebugOverlay::Box( vecBitePoint, -Vector(10,10,10), Vector(10,10,10), 0,255,0, 0, 0.1 );
 	//NDebugOverlay::Line( vecBitePoint, vecCheckPos, 0, 255, 0, true, 0.1 );

	if ( (vecBitePoint.x - vecCheckPos.x) > flDelta || (vecBitePoint.y - vecCheckPos.y) > flDelta )
	{
		// I can't bite this critter because it's not lined up with me on the X/Y plane. If it is 
		// as close to my mouth as I can get it, I should drop it.
		if( vecBitePoint.z - vecVictimPos.z < 72.0f )
		{
			// A man-sized target has been pulled up to my mouth, but 
			// is not aligned for biting. Drop it.
			SpitPrey();
		}

		return false;
	}

	// Right height?
	if ( (vecBitePoint.z - vecCheckPos.z) > flDelta )
	{
		// Slowly raise / lower the target into the right position
		if ( vecBitePoint.z > vecCheckPos.z )
		{
			// Pull the victim towards the mouth
			SetAltitude( m_flAltitude - 1 );
			vecVictimPos.z += 1;
		}
		else
		{
			// We pulled 'em up too far, so lower them a little
			SetAltitude( m_flAltitude + 1 );
			vecVictimPos.z -= 1;
		}
		UTIL_SetOrigin ( GetEnemy(), vecVictimPos );
		return false;
	}

	// Get the velocity of the bone we've grabbed, and only bite when it's not moving much
	CStudioHdr *pStudioHdr = m_hRagdoll->GetModelPtr();
	mstudiobone_t *pBone = pStudioHdr->pBone( m_iGrabbedBoneIndex );
	int iBoneIndex = pBone->physicsbone;
	ragdoll_t *pRagdoll = m_hRagdoll->GetRagdoll();
	IPhysicsObject *pRagdollPhys = pRagdoll->list[iBoneIndex].pObject;
	pRagdollPhys->GetVelocity( &vecVelocity, &angVel );
	return ( vecVelocity.LengthSqr() < 20 );
}


//-----------------------------------------------------------------------------
// Allows the physics prop to settle before biting it
//-----------------------------------------------------------------------------
bool CNPC_Barnacle::WaitForPhysicsObjectToSettle( float flBiteZOffset )
{
	--m_nShakeCount;
	if ( m_nShakeCount & 0x1 )
	{
		SetAltitude( flBiteZOffset + 15 );
	}
	else
	{
		SetAltitude( flBiteZOffset );
	}

	return ( m_nShakeCount <= 0 );


	/*
	IPhysicsObject *pPhysicsObject = GetEnemy()->VPhysicsGetObject();

	Vector vecVelocity;
	AngularImpulse angVel;
	pPhysicsObject->GetVelocity( &vecVelocity, &angVel );

	return ( vecVelocity.LengthSqr() < 25 );
	*/
}


//-----------------------------------------------------------------------------
// Purpose: Make a horrific noise before we pull the prey stuck to our tongue up towards our mouth
//-----------------------------------------------------------------------------
void CNPC_Barnacle::PlayLiftingScream( float flBiteZOffset )
{
	if ( !m_bPlayedPullSound && m_flAltitude < (flBiteZOffset + 100) )
	{
		EmitSound( "NPC_Barnacle.Scream" );
		m_bPlayedPullSound = true;
	}
}


//-----------------------------------------------------------------------------
// Purpose: Lift the prey stuck to our tongue up towards our mouth
//-----------------------------------------------------------------------------
void CNPC_Barnacle::PullEnemyTorwardsMouth( bool bAdjustEnemyOrigin )
{
	CBaseEntity *pEnemy = GetEnemy();
	if ( pEnemy->IsPlayer() && pEnemy->GetMoveType() == MOVETYPE_NOCLIP )
	{
		LostPrey( false );
		return;
	}

	// Pull the victim towards the mouth
	float dt = gpGlobals->curtime - GetLastThink();

	// Assumes constant frame rate :|
	m_flLocalTimer += dt;

	float flPull = fabs(sin( m_flLocalTimer * 5 ));

 	flPull *= m_flBarnaclePullSpeed * dt;

	SetAltitude( m_flAltitude - flPull );


	if ( bAdjustEnemyOrigin )
	{
		if ( m_flLastPull > 1.0 )
		{
			if ( (pEnemy->GetAbsOrigin() - m_vLastEnemyPos).LengthSqr() < Square( m_flLastPull - 1.0 ) )
			{
				if ( m_StuckTimer.Expired() )
				{
					LostPrey( false );
					return;
				}
			}
			else
			{
				m_StuckTimer.Set(3.0);
			}
		}
		else
			m_StuckTimer.Delay(dt);

		m_vLastEnemyPos = pEnemy->GetAbsOrigin();
		m_flLastPull = flPull;

		Vector vecNewPos = m_vLastEnemyPos;
		// vecNewPos.z += flPull;

#if 0
		// this is an example of one somewhat crude attempt to realign objects so that they are directly underneath
		// the barnacle. It introduces unacceptable oscillation.
		const float MAX_CENTERING_VELOCITY = 24.0f;
		float distToMove = MAX_CENTERING_VELOCITY * dt;
		Vector2D vToCenter = GetAbsOrigin().AsVector2D() - GetEnemy()->GetAbsOrigin().AsVector2D();
		float distFromCenter = vToCenter.NormalizeInPlace();

		Msg("<%.3f,%.3f>\n",vToCenter.x,vToCenter.y);

			
		if ( distFromCenter < distToMove )
		{
			vecNewPos.x = GetAbsOrigin().x;
			vecNewPos.y = GetAbsOrigin().y;
		}
		else
		{
			vToCenter *= distToMove;
			vecNewPos.x += vToCenter.x;
			vecNewPos.y += vToCenter.y;
			// GetEnemy()->Teleport( &vecNewPos, NULL, NULL );
		}	
	
#endif
		// recentering the player under the barnacle was tried in the code
		// below, but then disabled for Orange Box ship because the viewmodel
		// jitter became unacceptably noisy after other changes to physics
		// and client.
#if 0
		// this technique is a little noisy and needs to be readdressed.
		if (pEnemy->IsPlayer())
		{
			Vector playerOrigin = GetEnemy()->GetAbsOrigin();
			Vector2D vToCenter = GetAbsOrigin().AsVector2D() - playerOrigin.AsVector2D();
			float distFromCenter = vToCenter.NormalizeInPlace();

			// if we're off by more than a few inches
			if ( distFromCenter > 6.0f )
			{
				// get us there in a second
				Vector desiredVelocity;
				float distToMove = min(distFromCenter, 24.0f * dt);
				desiredVelocity.x = vToCenter.x * distToMove;
				desiredVelocity.y = vToCenter.y * distToMove;
				desiredVelocity.z = 0;
#if 0			// here is a physical force-based way (too noisy!):
				IPhysicsObject *pTonguePhys = m_hTongueTip->VPhysicsGetObject();
				pTonguePhys->ApplyForceCenter(desiredVelocity);
#else
				vecNewPos = playerOrigin + desiredVelocity;

				// find how far we can actually transport the player
				trace_t tr;
				UTIL_TraceEntity( pEnemy, playerOrigin, vecNewPos, MASK_PLAYERSOLID, m_hTongueTip.Get(), pEnemy->GetCollisionGroup(), &tr );
				pEnemy->Teleport(&tr.endpos, NULL, &desiredVelocity);
#endif
			}
		}

#endif

		// GetEnemy()->Teleport( &vecNewPos, NULL, NULL );

		if( pEnemy->GetFlags() & FL_ONGROUND )
		{
			// Try to fight OnGround
			pEnemy->SetGravity( 0 );
			pEnemy->RemoveFlag( FL_ONGROUND );
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CNPC_Barnacle::UpdatePlayerConstraint( void )
{
	// Check to see if the player's standing/ducking state has changed.
	CBasePlayer *pPlayer = static_cast<CBasePlayer*>( GetEnemy() );
	bool bStanding = ( ( pPlayer->GetFlags() & FL_DUCKING ) == 0 );
	if ( bStanding == m_bPlayerWasStanding )
		return;

	// if player is on the ladder, disengage him
	if ( pPlayer->GetMoveType() == MOVETYPE_LADDER )
	{
		pPlayer->ExitLadder();
	}

	// Destroy the current constraint.
	physenv->DestroyConstraint( m_pConstraint );
	m_pConstraint = NULL;

	if ( m_hTongueTip )
	{
		// Create the new constraint for the standing/ducking player physics object.
		IPhysicsObject *pPlayerPhys = pPlayer->VPhysicsGetObject();
		IPhysicsObject *pTonguePhys = m_hTongueTip->VPhysicsGetObject();
		
		constraint_fixedparams_t fixed;
		fixed.Defaults();
		fixed.InitWithCurrentObjectState( pTonguePhys, pPlayerPhys );
		fixed.constraint.Defaults();

		m_pConstraint = physenv->CreateFixedConstraint( pTonguePhys, pPlayerPhys, NULL, fixed );
	}

	// Save state for the next check.
	m_bPlayerWasStanding = bStanding;
}

//-----------------------------------------------------------------------------
// Purpose: Lift the prey stuck to our tongue up towards our mouth
//-----------------------------------------------------------------------------
void CNPC_Barnacle::LiftPlayer( float flBiteZOffset )
{
	// Add an additional height for the player to avoid view clipping
	flBiteZOffset += 25.0;

	// Play a scream when we're almost within bite range
	PlayLiftingScream( flBiteZOffset );

	// Update player constraint.
	UpdatePlayerConstraint();

	// Figure out when the prey has reached our bite range use eye position to avoid
	// clipping into the barnacle body
	if ( GetAbsOrigin().z - GetEnemy()->EyePosition().z < flBiteZOffset)
	{
		m_bLiftingPrey = false;

		// Start the bite animation. The anim event in it will finish the job.
		SetActivity( (Activity)ACT_BARNACLE_BITE_PLAYER );
	}
	else
	{
		PullEnemyTorwardsMouth( true );
	}
}


//-----------------------------------------------------------------------------
// Purpose: Lift the prey stuck to our tongue up towards our mouth
//-----------------------------------------------------------------------------
void CNPC_Barnacle::LiftNPC( float flBiteZOffset )
{
	// Necessary to make the NPCs not do things like talk
	GetEnemy()->AddEFlags( EFL_IS_BEING_LIFTED_BY_BARNACLE );

	// Play a scream when we're almost within bite range
	PlayLiftingScream( flBiteZOffset );

	// Figure out when the prey has reached our bite range
	if ( GetAbsOrigin().z - m_vecTip.Get().z < flBiteZOffset )
	{
		m_bLiftingPrey = false;

		const Vector &vecSize = GetEnemy()->CollisionProp()->OBBSize();
		if ( vecSize.z < 40 )
		{
			// Start the bite animation. The anim event in it will finish the job.
			SetActivity( (Activity)ACT_BARNACLE_BITE_SMALL_THINGS );
		}
		else
		{
			// Start the bite animation. The anim event in it will finish the job.
			SetActivity( (Activity)ACT_BARNACLE_BITE_HUMAN );
		}
	}
	else
	{
		PullEnemyTorwardsMouth( true );
	}
}


//-----------------------------------------------------------------------------
// Purpose: Lift the prey stuck to our tongue up towards our mouth
//-----------------------------------------------------------------------------
void CNPC_Barnacle::LiftRagdoll( float flBiteZOffset )
{
	// Necessary to make the NPCs not do things like talk
	GetEnemy()->AddEFlags( EFL_IS_BEING_LIFTED_BY_BARNACLE );

	// Play a scream when we're almost within bite range
	PlayLiftingScream( flBiteZOffset );

	// Figure out when the prey has reached our bite range
	if ( GetAbsOrigin().z - m_vecTip.Get().z < flBiteZOffset )
	{
		// If we've got a ragdoll, wait until the bone is down below the mouth.
 		if ( !WaitForRagdollToSettle( flBiteZOffset ) )
			return;

  		if ( GetEnemy()->Classify() == CLASS_ZOMBIE )
		{
			// lifted the prey high enough to see it's a zombie. Spit it out.
			if ( hl2_episodic.GetBool() )
			{
				m_bLiftingPrey = false;
				SetActivity( (Activity)ACT_BARNACLE_BITE_SMALL_THINGS );
			}
			else
			{
				SpitPrey();
			}
			return;
		}

		m_bLiftingPrey = false;

		const Vector &vecSize = GetEnemy()->CollisionProp()->OBBSize();
		if ( vecSize.z < 40 )
		{
			// Start the bite animation. The anim event in it will finish the job.
			SetActivity( (Activity)ACT_BARNACLE_BITE_SMALL_THINGS );
		}
		else
		{
			// Start the bite animation. The anim event in it will finish the job.
			SetActivity( (Activity)ACT_BARNACLE_BITE_HUMAN );
		}
	}
	else
	{
		// Pull the victim towards the mouth
		PullEnemyTorwardsMouth( false );

		// Apply forces to the attached ragdoll based upon the animations of the enemy, if the enemy is still alive.
		if ( GetEnemy()->IsAlive() )
		{
			CBaseAnimating *pAnimating = dynamic_cast<CBaseAnimating*>( GetEnemy() );

			// Get the current bone matrix
			/*
			Vector pos[MAXSTUDIOBONES];
			Quaternion q[MAXSTUDIOBONES];
			matrix3x4_t pBoneToWorld[MAXSTUDIOBONES];
			CalcPoseSingle( pStudioHdr, pos, q, pAnimating->GetSequence(), pAnimating->m_flCycle, pAnimating->GetPoseParameterArray(), BONE_USED_BY_ANYTHING );
			Studio_BuildMatrices( pStudioHdr, vec3_angle, vec3_origin, pos, q, -1, pBoneToWorld, BONE_USED_BY_ANYTHING );


			// Apply the forces to the ragdoll
			RagdollApplyAnimationAsVelocity( *(m_hRagdoll->GetRagdoll()), pBoneToWorld );
			*/

			// Get the current bone matrix
			matrix3x4_t pBoneToWorld[MAXSTUDIOBONES];
			pAnimating->SetupBones( pBoneToWorld, BONE_USED_BY_ANYTHING );

			// Apply the forces to the ragdoll
			RagdollApplyAnimationAsVelocity( *(m_hRagdoll->GetRagdoll()), m_pRagdollBones, pBoneToWorld, 0.2 );

			// Store off the current bone matrix for next time
			pAnimating->SetupBones( m_pRagdollBones, BONE_USED_BY_ANYTHING );
		}
	}
}


//-----------------------------------------------------------------------------
// Purpose: Lift the prey stuck to our tongue up towards our mouth
//-----------------------------------------------------------------------------
void CNPC_Barnacle::LiftPhysicsObject( float flBiteZOffset )
{
	CBaseEntity *pVictim = GetEnemy();

	// Bite a little higher up, since the bits point is the tip of the tongue
	flBiteZOffset -= 5.0f;

	//NDebugOverlay::Box( vecCheckPos, -Vector(10,10,10), Vector(10,10,10), 255,255,255, 0, 0.1 );

	// Play a scream when we're almost within bite range
	PlayLiftingScream( flBiteZOffset );

	// Figure out when the prey has reached our bite range
	if ( GetAbsOrigin().z - m_vecTip.Get().z < flBiteZOffset ) // then yes, let's chomp
	{
		if ( m_hTongueTip )
		{
			m_hTongueTip->m_pSpring->SetSpringConstant( BARNACLE_TONGUE_SPRING_CONSTANT_HANGING );
		}

		// Wait until the physics object stops flailing
		if ( !WaitForPhysicsObjectToSettle( flBiteZOffset ) )
			return;

		// Necessary for good +use interactions
		pVictim->RemoveEFlags( EFL_IS_BEING_LIFTED_BY_BARNACLE );

		// If we got a physics prop, wait until the thing has settled down
		m_bLiftingPrey = false;

		if ( hl2_episodic.GetBool() )
		{
			CBounceBomb *pBounce = dynamic_cast<CBounceBomb *>( pVictim );

			if ( pBounce )
			{
				if ( m_bSwallowingBomb == true )
				{
					pBounce->ExplodeThink();
					return;
				}

				SetActivity( (Activity)ACT_BARNACLE_BITE_SMALL_THINGS );
			}
			else
			{
				// Start the bite animation. The anim event in it will finish the job.
				SetActivity( (Activity)ACT_BARNACLE_TASTE_SPIT );
			}
		}
		else
		{
			// Start the bite animation. The anim event in it will finish the job.
			SetActivity( (Activity)ACT_BARNACLE_TASTE_SPIT );
		}
		
#ifdef HL2_EPISODIC
		// if the object is a combatclass, send it a chomp interaction in case it wants to respond to that
		// in some nonstandard way.
		CBaseCombatCharacter *pBCC = dynamic_cast<CBaseCombatCharacter *>(pVictim);
		if( pBCC ) 
		{
			Vector tipPos = m_vecTip.Get();

			pBCC->DispatchInteraction( g_interactionBarnacleVictimBite, &tipPos, this );
		}
#endif
	}
	else
	{
		// Necessary for good +use interactions
		pVictim->AddEFlags( EFL_IS_BEING_LIFTED_BY_BARNACLE );

		// Pull the victim towards the mouth
		PullEnemyTorwardsMouth( false );
	}
}


//-----------------------------------------------------------------------------
// Purpose: Lift the prey stuck to our tongue up towards our mouth
//-----------------------------------------------------------------------------
void CNPC_Barnacle::LiftPrey( void )
{
	CBaseEntity *pVictim = GetEnemy();
	Assert( pVictim );

	// Drop the prey if it's been obscured by something
	trace_t tr;
	AI_TraceLine( WorldSpaceCenter(), pVictim->WorldSpaceCenter(), MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );
	bool bEnemyIsNPC = IsEnemyAnNPC() && !IsEnemyARagdoll();
	if ( ( bEnemyIsNPC && !pVictim->IsAlive() ) || (tr.fraction < 1.0 && tr.m_pEnt != pVictim && tr.m_pEnt != m_hRagdoll) )
	{
		if ( !GetEnemy()->IsPlayer() )
		{
			// ignore the object so we don't get into a loop of trying to pick it up.
			m_hLastSpitEnemy = GetEnemy();
		}
		LostPrey( false );
		return;
	}

	// Height from the barnacle's origin to the point at which it bites
	float flBiteZOffset = 60.0;

	if ( IsEnemyAPlayer() )
	{
		LiftPlayer(flBiteZOffset);
	}
	else if ( IsEnemyARagdoll() )
	{
		LiftRagdoll(flBiteZOffset);
	}
	else if ( bEnemyIsNPC )
	{
		LiftNPC(flBiteZOffset);
	}
	else
	{
		LiftPhysicsObject(flBiteZOffset);
	}

	if ( m_hRagdoll )
	{
		QAngle newAngles( 0, m_hRagdoll->GetAbsAngles()[YAW], 0 );

		Vector centerDelta = m_hRagdoll->WorldSpaceCenter() - GetEnemy()->WorldSpaceCenter();
		Vector newOrigin = GetEnemy()->GetAbsOrigin() + centerDelta;
		GetEnemy()->SetAbsOrigin( newOrigin );
		GetEnemy()->SetAbsAngles( newAngles );
	}
}

//-----------------------------------------------------------------------------
// Purpose: Attach a serverside ragdoll prop for the specified entity to our tongue
//-----------------------------------------------------------------------------
CRagdollProp *CNPC_Barnacle::AttachRagdollToTongue( CBaseAnimating *pAnimating )
{
	// Find his head bone
	m_iGrabbedBoneIndex = -1;
	Vector vecNeckOffset;
	
	if ( m_hTongueTip )
	{
		vecNeckOffset = (pAnimating->EyePosition() - m_hTongueTip->GetAbsOrigin());
	}

	CStudioHdr *pHdr = pAnimating->GetModelPtr();
	if ( pHdr )
	{
		int set = pAnimating->GetHitboxSet();
		for( int i = 0; i < pHdr->iHitboxCount(set); i++ )
		{
			mstudiobbox_t *pBox = pHdr->pHitbox( i, set );
			if ( !pBox )
				continue;

			if ( pBox->group == HITGROUP_HEAD )
			{
				m_iGrabbedBoneIndex = pBox->bone;
				break;
			}
		}
	}

	// HACK: Until we have correctly assigned hitgroups on our models, lookup the bones
	//		 for the models that we know are in the barnacle maps.
	//m_iGrabbedBoneIndex = pAnimating->LookupBone( "Bip01 L Foot" );
	if ( m_iGrabbedBoneIndex == -1 )
	{
 		// Citizens, Conscripts
		m_iGrabbedBoneIndex = pAnimating->LookupBone( "Bip01 Head" );
	}
	if ( m_iGrabbedBoneIndex == -1 )
	{
		// Metrocops, Combine soldiers
		m_iGrabbedBoneIndex = pAnimating->LookupBone( "ValveBiped.Bip01_Head1" );
	}
	if ( m_iGrabbedBoneIndex == -1 )
	{
		// Vortigaunts
		m_iGrabbedBoneIndex = pAnimating->LookupBone( "ValveBiped.head" );
	}
	if ( m_iGrabbedBoneIndex == -1 )
	{
		// Bullsquids
		m_iGrabbedBoneIndex = pAnimating->LookupBone( "Bullsquid.Head_Bone1" );
	}

	if ( m_iGrabbedBoneIndex == -1 )
	{
		// Just use the first bone
		m_iGrabbedBoneIndex = 0;
	}

	// Move the tip to the bone
	Vector vecBonePos;
	QAngle vecBoneAngles;
	pAnimating->GetBonePosition( m_iGrabbedBoneIndex, vecBonePos, vecBoneAngles );

	if ( m_hTongueTip )
	{
		m_hTongueTip->Teleport( &vecBonePos, NULL, NULL );
	}

	//NDebugOverlay::Box( vecBonePos, -Vector(5,5,5), Vector(5,5,5), 255,255,255, 0, 10.0 );

	// Create the ragdoll attached to tongue
	IPhysicsObject *pTonguePhysObject = m_hTongueTip->VPhysicsGetObject();
	CRagdollProp *pRagdoll = CreateServerRagdollAttached( pAnimating, vec3_origin, -1, COLLISION_GROUP_NONE, pTonguePhysObject, m_hTongueTip, 0, vecBonePos, m_iGrabbedBoneIndex, vec3_origin );
	if ( pRagdoll )
	{
#if HL2_EPISODIC
		PhysEnableEntityCollisions( this, pAnimating );
		PhysDisableEntityCollisions( this, pRagdoll );
#endif

		pRagdoll->DisableAutoFade();
		pRagdoll->SetThink( NULL );
	}

	return pRagdoll;
}

void CNPC_Barnacle::InputSetDropTongueSpeed( inputdata_t &inputdata )
{
	m_flBarnaclePullSpeed = inputdata.value.Int();
}

void CNPC_Barnacle::InputDropTongue( inputdata_t &inputdata )
{
	DropTongue();
}

//-----------------------------------------------------------------------------
// Purpose: Grab the specified target with our tongue
//-----------------------------------------------------------------------------
void CNPC_Barnacle::AttachTongueToTarget( CBaseEntity *pTouchEnt, Vector vecGrabPos )
{

#if HL2_EPISODIC
	m_OnGrab.Set( pTouchEnt, this, this );
#endif

	// Reset this valricue each time we attach prey. If it needs to be reduced, code below will do so.
	m_flBarnaclePullSpeed = BARNACLE_PULL_SPEED;

	if ( RandomFloat(0,1) > 0.5 )
	{
		EmitSound( "NPC_Barnacle.PullPant" );
	}
	else
	{
		EmitSound( "NPC_Barnacle.TongueStretch" );
	}

	SetActivity( (Activity)ACT_BARNACLE_SLURP );

	// Get the player out of the vehicle he's in.
	if ( pTouchEnt->IsPlayer() )
	{
		CBasePlayer *pPlayer = static_cast<CBasePlayer*>(pTouchEnt);
		if ( pPlayer->IsInAVehicle() )
		{
			pPlayer->LeaveVehicle( pPlayer->GetAbsOrigin(), pPlayer->GetAbsAngles() );

			// The player could have warped through the tongue while on a high-speed vehicle.
			// Move him back under the barnacle.
			Vector vecDelta;
			VectorSubtract( pPlayer->GetAbsOrigin(), GetAbsOrigin(), vecDelta );
			vecDelta.z = 0.0f;
			float flDist = VectorNormalize( vecDelta );
			if ( flDist > 20 )
			{
				Vector vecNewPos;
				VectorMA( GetAbsOrigin(), 20, vecDelta, vecNewPos );
				vecNewPos.z = pPlayer->GetAbsOrigin().z;
				pPlayer->SetAbsOrigin( vecNewPos );
			}
		}

		m_bPlayerWasStanding = ( ( pPlayer->GetFlags() & FL_DUCKING ) == 0 );
	}

	SetEnemy( pTouchEnt );
#if HL2_EPISODIC
	// Disable collision between myself and the obejct I've seized.
	PhysDisableEntityCollisions( this, pTouchEnt );
#endif

	// teleporting the player in this way is illegitimate -- try it in third person to see the problem
	if ( /* pTouchEnt->IsPlayer() || */ pTouchEnt->MyNPCPointer() )
	{
		Vector origin = GetAbsOrigin();
		origin.z = pTouchEnt->GetAbsOrigin().z;

		CTraceFilterSkipTwoEntities traceFilter( this, pTouchEnt, COLLISION_GROUP_NONE );
		trace_t placementTrace;
		UTIL_TraceHull( origin, origin, pTouchEnt->WorldAlignMins(), pTouchEnt->WorldAlignMaxs(), MASK_NPCSOLID, &traceFilter, &placementTrace );
		if ( placementTrace.startsolid )
		{
			UTIL_TraceHull( origin + Vector(0, 0, 24), origin, pTouchEnt->WorldAlignMins(), pTouchEnt->WorldAlignMaxs(), MASK_NPCSOLID, &traceFilter, &placementTrace );
			if ( !placementTrace.startsolid )
			{
				pTouchEnt->SetAbsOrigin( placementTrace.endpos );
				// pTouchEnt->Teleport( &placementTrace.endpos, NULL, NULL );
			}
		}
		else
		{
			pTouchEnt->SetAbsOrigin( origin );
			// pTouchEnt->Teleport( &origin, NULL, NULL );
		}
	}

	m_nShakeCount = 6;
	m_bLiftingPrey = true;// indicate that we should be lifting prey.
	SetAltitude( (GetAbsOrigin().z - vecGrabPos.z) );
	m_bPlayedPullSound  = false;

	CBaseAnimating *pAnimating = dynamic_cast<CBaseAnimating*>(pTouchEnt);

	if ( IsEnemyAPlayer() || IsEnemyAPhysicsObject() )
	{
		// The player (and phys objects) doesn't ragdoll, so just grab him and pull him up manually
		IPhysicsObject *pPlayerPhys = pTouchEnt->VPhysicsGetObject();
		IPhysicsObject *pTonguePhys = m_hTongueTip->VPhysicsGetObject();

		Vector vecGrabPos;
		if ( pTouchEnt->IsPlayer() )
		{
			vecGrabPos = pTouchEnt->EyePosition();
#if BARNACLE_USE_TONGUE_OFFSET
			VectorRotate( m_svPlayerHeldTipOffset, pTouchEnt->EntityToWorldTransform(), m_vecTipDrawOffset.GetForModify() );
			m_vecTipDrawOffset.GetForModify().z = m_svPlayerHeldTipOffset.z;
#endif
			// pTonguePhys->GetPosition(&vecGrabPos,NULL);
		}
		else
		{
			VectorSubtract( m_vecTip, pTouchEnt->GetAbsOrigin(), vecGrabPos	);
			VectorNormalize( vecGrabPos );
			vecGrabPos = physcollision->CollideGetExtent( pPlayerPhys->GetCollide(), pTouchEnt->GetAbsOrigin(), pTouchEnt->GetAbsAngles(), vecGrabPos );
#if BARNACLE_USE_TONGUE_OFFSET
			m_vecTipDrawOffset.GetForModify().Zero();
#endif
		}

		m_hTongueTip->Teleport( &vecGrabPos, NULL, NULL );

		float flDist = (vecGrabPos - GetAbsOrigin() ).Length();
		float flTime = flDist / m_flBarnaclePullSpeed;

		// If this object would be pulled in too quickly, change the pull speed.
		if( flTime < BARNACLE_MIN_PULL_TIME )
		{
			m_flBarnaclePullSpeed = flDist / BARNACLE_MIN_PULL_TIME;
		}

		constraint_fixedparams_t fixed;
		fixed.Defaults();
		fixed.InitWithCurrentObjectState( pTonguePhys, pPlayerPhys );
		fixed.constraint.Defaults();
		
		/*
You can use this stanza to try to counterplace the constraint on the player's head so he gets hauled sideways to the right place on the barnacle, but it is better to just move the tongue before attachment.
		if ( IsEnemyAPlayer() )
		{
			Vector2D vToCenter = GetAbsOrigin().AsVector2D() - pTouchEnt->EyePosition().AsVector2D();
			fixed.attachedRefXform[0][3] -= vToCenter.x ;
			fixed.attachedRefXform[1][3] -= vToCenter.y ;
		}
		*/
		
		m_pConstraint = physenv->CreateFixedConstraint( pTonguePhys, pPlayerPhys, NULL, fixed );

		// Increase the tongue's spring constant while lifting 
		m_hTongueTip->m_pSpring->SetSpringConstant( BARNACLE_TONGUE_SPRING_CONSTANT_LIFTING );
		UpdateTongue();

		return;
	}

	// NPC case...
	pAnimating->InvalidateBoneCache();

	// Make a ragdoll for the guy, and hide him.
	pTouchEnt->AddSolidFlags( FSOLID_NOT_SOLID );

  	m_hRagdoll = AttachRagdollToTongue( pAnimating );
	m_hRagdoll->SetDamageEntity( pAnimating );

	// Make it try to blend out of ragdoll on the client on deletion
	// NOTE: This isn't fully implemented, so disable
	//m_hRagdoll->SetUnragdoll( pAnimating );

	// Apply the target's current velocity to each of the ragdoll's bones
	Vector vecVelocity = pAnimating->GetGroundSpeedVelocity() * 0.5;
	ragdoll_t *pRagdoll = m_hRagdoll->GetRagdoll();
	
	// barnacle might let go if ragdoll is separated - so increase the separation checking a bit
	constraint_groupparams_t params;
	pRagdoll->pGroup->GetErrorParams( &params );
	params.minErrorTicks = min( params.minErrorTicks, 5 );
	pRagdoll->pGroup->SetErrorParams( params );

	for ( int i = 0; i < pRagdoll->listCount; i++ )
	{
		pRagdoll->list[i].pObject->AddVelocity( &vecVelocity, NULL );
	}

	if ( npc_barnacle_swallow.GetBool() )
	{
		m_hRagdoll->SetOverlaySequence( ACT_GESTURE_BARNACLE_STRANGLE );
		m_hRagdoll->SetBlendWeight( 1.0f );
	}

	// Now hide the actual enemy
	pTouchEnt->AddEffects( EF_NODRAW );

	// Increase the tongue's spring constant while lifting 
	m_hTongueTip->m_pSpring->SetSpringConstant( BARNACLE_TONGUE_SPRING_CONSTANT_LIFTING );
	UpdateTongue();

	// Store off the current bone matrix so we have it next frame
	pAnimating->SetupBones( m_pRagdollBones, BONE_USED_BY_ANYTHING );
}


//-----------------------------------------------------------------------------
// Spit out the prey; add physics force!
//-----------------------------------------------------------------------------
void CNPC_Barnacle::SpitPrey()
{
	if ( GetEnemy() )
	{
		IPhysicsObject *pObject = GetEnemy()->VPhysicsGetObject();
		if (pObject)
		{
			Vector vecPosition, force;
			GetAttachment( m_nSpitAttachment, vecPosition, &force );

			force *= pObject->GetMass() * 50.0f;
			pObject->ApplyForceOffset( force, vec3_origin );
		}

		m_hLastSpitEnemy = GetEnemy();
	}

	LostPrey( false );
}


//-----------------------------------------------------------------------------
// Purpose: Prey is in position, bite them and start swallowing them
//-----------------------------------------------------------------------------
void CNPC_Barnacle::BitePrey( void )
{
	Assert( GetEnemy() );

	CBaseCombatCharacter *pVictim = GetEnemyCombatCharacterPointer();

#ifdef HL2_EPISODIC
 	if ( pVictim == NULL )
	{
		if ( GetEnemy() )
		{
			CBounceBomb *pBounce = dynamic_cast<CBounceBomb *>( GetEnemy() );

			if ( pBounce )
			{
				// Stop the ragdoll moving and start to pull the sucker up into our mouth
				m_bSwallowingPrey = true;
				m_bSwallowingBomb = true;
				IPhysicsObject *pTonguePhys = m_hTongueTip->VPhysicsGetObject();

				// Stop the tongue's spring getting in the way of swallowing
				m_hTongueTip->m_pSpring->SetSpringConstant( 0 );

				// Switch the tongue tip to shadow and drag it up
				pTonguePhys->SetShadow( 1e4, 1e4, false, false );
				pTonguePhys->UpdateShadow( m_hTongueTip->GetAbsOrigin(), m_hTongueTip->GetAbsAngles(), false, 0 );
				m_hTongueTip->SetMoveType( MOVETYPE_NOCLIP );
				m_hTongueTip->SetAbsVelocity( Vector(0,0,32) );
				

				SetAltitude( (GetAbsOrigin().z - m_hTongueTip->GetAbsOrigin().z) );
			}
		}
		
		return;
	}
#endif
	
	Assert( pVictim );
	if ( !pVictim )
	{	
		return;
	}

	EmitSound( "NPC_Barnacle.FinalBite" );

	m_flVictimHeight = GetEnemy()->WorldAlignSize().z;

	// Kill the victim instantly
	int iDamageType = DMG_SLASH | DMG_ALWAYSGIB;
	int nDamage; 
	if ( !pVictim->IsPlayer() )
	{
		iDamageType |= DMG_ALWAYSGIB;
		nDamage = pVictim->m_iHealth; 
	}
	else
	{
		nDamage = BARNACLE_BITE_DAMAGE_TO_PLAYER; 
	}

	if ( m_hRagdoll )
	{
		// We've got a ragdoll, so prevent this creating another one
		iDamageType |= DMG_REMOVENORAGDOLL;
		m_hRagdoll->SetDamageEntity( NULL );
	}

	
#if HL2_EPISODIC
	m_bSwallowingPoison = IsPoisonous(pVictim);
	unsigned int enemyClass = GetEnemy()->Classify();
#endif
	// DMG_CRUSH because we don't wan't to impart physics forces

	pVictim->TakeDamage( CTakeDamageInfo( this, this, nDamage, iDamageType | DMG_CRUSH ) );

	m_cGibs = 3;

	// In episodic, bite the zombie's headcrab off & drop the body
#ifdef HL2_EPISODIC

	if ( enemyClass == CLASS_ZOMBIE )
	{
		if ( m_hRagdoll )
		{
			m_hRagdoll->SetBodygroup( ZOMBIE_BODYGROUP_HEADCRAB, false );
			DetachAttachedRagdoll( m_hRagdoll );
			m_hLastSpitEnemy = m_hRagdoll.Get();
			m_hRagdoll->EmitSound( "NPC_HeadCrab.Die" );
			m_hRagdoll = NULL;
		}

		// Create some blood to hide the vanishing headcrab
		Vector vecBloodPos;
		CollisionProp()->NormalizedToWorldSpace( Vector( 0.5f, 0.5f, 0.0f ), &vecBloodPos );
		UTIL_BloodSpray( vecBloodPos, Vector(0,0,-1), GetEnemy()->BloodColor(), 8, FX_BLOODSPRAY_ALL );
		
		m_flDigestFinish = gpGlobals->curtime + 10.0;
		return;
	}

	// in episodic, where barnacles can eat antlions, vanish the ragdoll because the gibs will spray everywhere
	// and hide it.
	if ( enemyClass == CLASS_ANTLION )
	{
		
#ifndef _XBOX
		m_nBloodColor = pVictim->BloodColor(); 
#endif
		m_flNextBloodTime = 0.0f;
		SprayBlood();

		m_flDigestFinish = gpGlobals->curtime + 10.0;
		if (m_hRagdoll)
		{
			UTIL_Remove( m_hRagdoll );
		}


		if ( m_bSwallowingPoison )
		{	// hurt me
			TakeDamage( CTakeDamageInfo( this, this, m_iHealth, DMG_ACID ) );
		}

		return;
	}

#endif

	// Players are never swallowed, nor is anything we don't have a ragdoll for
	if ( !m_hRagdoll || pVictim->IsPlayer() )
	{
		if ( !pVictim->IsPlayer() || pVictim->GetHealth() <= 0 )
		{
			LostPrey( false );
		}
		return;
	}

	// Stop the ragdoll moving and start to pull the sucker up into our mouth
	m_bSwallowingPrey = true;
	IPhysicsObject *pTonguePhys = m_hTongueTip->VPhysicsGetObject();

	// Make it nonsolid to the world so we can pull it through the roof
	PhysDisableEntityCollisions( m_hRagdoll->VPhysicsGetObject(), g_PhysWorldObject );

	// Stop the tongue's spring getting in the way of swallowing
	m_hTongueTip->m_pSpring->SetSpringConstant( 0 );

	// Switch the tongue tip to shadow and drag it up
	pTonguePhys->SetShadow( 1e4, 1e4, false, false );
	pTonguePhys->UpdateShadow( m_hTongueTip->GetAbsOrigin(), m_hTongueTip->GetAbsAngles(), false, 0 );
	m_hTongueTip->SetMoveType( MOVETYPE_NOCLIP );
	m_hTongueTip->SetAbsVelocity( Vector(0,0,32) );

	SetAltitude( (GetAbsOrigin().z - m_hTongueTip->GetAbsOrigin().z) );

	if ( !npc_barnacle_swallow.GetBool() )
		return;

	// Because the victim is dead, remember the blood color
	m_flNextBloodTime = 0.0f;
	
	// NOTE: This was too confusing to people with the more recognizable blood -- jdw
#ifndef _XBOX
	m_nBloodColor = pVictim->BloodColor(); 
#endif
	CollisionProp()->NormalizedToWorldSpace( Vector( 0.5f, 0.5f, 0.0f ), &m_vecBloodPos );

	// m_hRagdoll->SetOverlaySequence( ACT_DIE_BARNACLE_SWALLOW );
	m_hRagdoll->SetBlendWeight( 0.0f );

	SprayBlood();
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CNPC_Barnacle::SprayBlood()
{
	if ( gpGlobals->curtime < m_flNextBloodTime )
		return;

	m_flNextBloodTime = gpGlobals->curtime + 0.2f;

	Vector bloodDir = RandomVector( -1.0f, 1.0f );
	bloodDir.z = -fabs( bloodDir.z );

	Vector jitterPos = RandomVector( -8, 8 );
	jitterPos.z = 0.0f;

#ifndef _XBOX
	UTIL_BloodSpray( m_vecBloodPos + jitterPos, Vector( 0,0,-1),
		m_nBloodColor, RandomInt( 4, 8 ), RandomInt(0,2) == 0 ? FX_BLOODSPRAY_ALL : FX_BLOODSPRAY_CLOUD );
#else
	UTIL_BloodSpray( m_vecBloodPos + jitterPos, Vector( 0,0,-1),
		BLOOD_COLOR_YELLOW, RandomInt( 4, 8 ), RandomInt(0,2) == 0 ? FX_BLOODSPRAY_ALL : FX_BLOODSPRAY_CLOUD );
#endif
}

//-----------------------------------------------------------------------------
// Purpose: Slowly swallow the prey whole. Only used on humanoids.
//-----------------------------------------------------------------------------
void CNPC_Barnacle::SwallowPrey( void )
{
	if ( IsActivityFinished() )
	{
		if (GetActivity() == ACT_BARNACLE_BITE_HUMAN )
		{
			SetActivity( (Activity)ACT_BARNACLE_CHEW_HUMAN );
		}
		else
		{
			SetActivity( (Activity)ACT_BARNACLE_CHEW_SMALL_THINGS );
		}
	}

	// Move the body up slowly
	Vector vecSwallowPos = m_hTongueTip->GetAbsOrigin();
	vecSwallowPos.z -= m_flVictimHeight;
	//NDebugOverlay::Box( vecSwallowPos, -Vector(5,5,5), Vector(5,5,5), 255,255,255, 0, 0.1 );

	// bite prey every once in a while
	if ( random->RandomInt(0,25) == 0 )
	{
		EmitSound( "NPC_Barnacle.Digest" );
	}

	// Fully swallowed it?
	float flDistanceToGo = GetAbsOrigin().z - vecSwallowPos.z;
	if ( flDistanceToGo <= 0 )
	{
		// He's dead jim
		m_bSwallowingPrey = false;
		m_hTongueTip->SetAbsVelocity( vec3_origin );

#if HL2_EPISODIC
		// digest poisonous things for just a moment before being killed by them (it looks wierd if it's instant)
		m_flDigestFinish = gpGlobals->curtime + m_bSwallowingPoison ? 0.48f : 10.0f;
#else
		m_flDigestFinish = gpGlobals->curtime + 10.0;
#endif
	}

	if ( npc_barnacle_swallow.GetBool() )
	{
		SprayBlood();
	}
}

//-----------------------------------------------------------------------------
// Purpose: Remove the fake ragdoll and bring the actual enemy back in view
//-----------------------------------------------------------------------------
void CNPC_Barnacle::RemoveRagdoll( bool bDestroyRagdoll )
{
	// Destroy the tongue tip constraint
  	if ( m_pConstraint )
  	{
  		physenv->DestroyConstraint( m_pConstraint );
  		m_pConstraint = NULL;
  	}

	// Remove the ragdoll
	if ( m_hRagdoll )
	{
		// Only destroy the ragdoll if told to. We might be just dropping
		// the ragdoll because the target was killed on the way up.
		m_hRagdoll->SetDamageEntity( NULL );
		if ( npc_barnacle_swallow.GetBool() )
		{
			m_hRagdoll->SetThink( NULL );
			m_hRagdoll->SetBlendWeight( 1.0f );
		}
		DetachAttachedRagdoll( m_hRagdoll );
		if ( bDestroyRagdoll )
		{
			UTIL_Remove( m_hRagdoll );
		}
		m_hRagdoll = NULL;

		// Reduce the spring constant while we lower
		m_hTongueTip->m_pSpring->SetSpringConstant( BARNACLE_TONGUE_SPRING_CONSTANT_LOWERING );

		// Unhide the enemy
		if ( GetEnemy() )
		{
			GetEnemy()->RemoveEffects( EF_NODRAW );
			GetEnemy()->RemoveSolidFlags( FSOLID_NOT_SOLID );
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: For some reason (he was killed, etc) we lost the prey we were dragging towards our mouth.
//-----------------------------------------------------------------------------
void CNPC_Barnacle::LostPrey( bool bRemoveRagdoll )
{
	
#if HL2_EPISODIC
	m_OnRelease.Set( GetEnemy(), this, this );
#endif

	CBaseEntity * const pEnemy = GetEnemy();

 	if ( pEnemy )
	{
#if HL2_EPISODIC
		PhysEnableEntityCollisions( this, pEnemy );
#endif

		//No one survives being snatched by a barnacle anymore, so leave
		// this flag set so that their entity gets removed.
		//GetEnemy()->RemoveEFlags( EFL_IS_BEING_LIFTED_BY_BARNACLE );
		CBaseCombatCharacter *pVictim = GetEnemyCombatCharacterPointer();
		if ( pVictim )
		{
			pVictim->DispatchInteraction( g_interactionBarnacleVictimReleased, NULL, this );
			pVictim->RemoveEFlags( EFL_IS_BEING_LIFTED_BY_BARNACLE );

			if ( m_hRagdoll )
			{
				QAngle newAngles( 0, m_hRagdoll->GetAbsAngles()[ YAW ], 0 );

				Vector centerDelta = m_hRagdoll->WorldSpaceCenter() - pEnemy->WorldSpaceCenter();
				Vector newOrigin = pEnemy->GetAbsOrigin() + centerDelta;
				pEnemy->SetAbsOrigin( newOrigin );

				pVictim->SetAbsAngles( newAngles );
			}
			pVictim->SetGroundEntity( NULL );
		}
		else if ( IsEnemyAPhysicsObject() )
		{
			// If we're a physics object, then we need to clear this flag
			pEnemy->RemoveEFlags( EFL_IS_BEING_LIFTED_BY_BARNACLE );
		}
	}


	RemoveRagdoll( bRemoveRagdoll );
	m_bLiftingPrey = false;
	m_bSwallowingPrey = false;
#if HL2_EPISODIC
	m_bSwallowingPoison = false;
#endif
	SetEnemy( NULL );


	m_vecTipDrawOffset.GetForModify().Zero();

	if ( m_hTongueTip )
	{
		// Remove our tongue's shadow object, in case we just finished swallowing something
		IPhysicsObject *pPhysicsObject = m_hTongueTip->VPhysicsGetObject();
		if ( pPhysicsObject && pPhysicsObject->GetShadowController() )
		{
			Vector vecCenter = WorldSpaceCenter();
			m_hTongueTip->Teleport( &vecCenter, NULL, &vec3_origin );

			// Reduce the spring constant while we lower
			m_hTongueTip->m_pSpring->SetSpringConstant( BARNACLE_TONGUE_SPRING_CONSTANT_LOWERING );

			// Start colliding with the world again
			pPhysicsObject->RemoveShadowController();
			m_hTongueTip->SetMoveType( MOVETYPE_VPHYSICS );
			pPhysicsObject->EnableMotion( true );
			pPhysicsObject->EnableGravity( true );
			pPhysicsObject->RecheckCollisionFilter();
		}
	}
}


//-----------------------------------------------------------------------------
// The tongue's vphysics updated
//-----------------------------------------------------------------------------
void CNPC_Barnacle::OnTongueTipUpdated()
{
	// Update the tip's position
	const Vector &vecNewTip = m_hTongueTip->GetAbsOrigin();
	if ( vecNewTip != m_vecTip )
	{
		m_vecTip = vecNewTip;
		CollisionProp()->MarkSurroundingBoundsDirty();
	}
}


//-----------------------------------------------------------------------------
// Purpose: Update the positions of the tongue points
//-----------------------------------------------------------------------------
void CNPC_Barnacle::UpdateTongue( void )
{
	if ( m_hTongueTip == NULL )
		return;

	// Set the spring's length to that of the tongue's extension

	// Compute the rest length of the tongue based on the spring. 
	// This occurs when mg == kx or x = mg/k
	float flRestStretch = (BARNACLE_TONGUE_TIP_MASS * sv_gravity.GetFloat()) / BARNACLE_TONGUE_SPRING_CONSTANT_HANGING;

	// FIXME: HACK!!!! The code above doesn't quite make the tip end up in the right place.
	// but it should. So, we're gonna hack it the rest of the way.
	flRestStretch += 4;

	m_hTongueTip->m_pSpring->SetSpringLength( m_flAltitude - flRestStretch );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CNPC_Barnacle::SpawnDeathGibs( void )
{
	bool bDroppedAny = false;

	// Drop a random number of gibs
	for ( size_t i=0; i < ARRAYSIZE(m_szGibNames); i++ )
	{
		if ( random->RandomInt( 0, 1 ) )
		{
			CGib::SpawnSpecificGibs( this, 1, 32, 1, m_szGibNames[i] );
			bDroppedAny = true;
		}
	}

	// Make sure we at least drop something
	if ( bDroppedAny == false )
	{
		CGib::SpawnSpecificGibs( this, 1, 32, 1, m_szGibNames[0] );
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CNPC_Barnacle::Event_Killed( const CTakeDamageInfo &info )
{
	m_OnDeath.FireOutput( info.GetAttacker(), this );
	SendOnKilledGameEvent( info );

	AddSolidFlags( FSOLID_NOT_SOLID );
	m_takedamage = DAMAGE_NO;
	m_lifeState	= LIFE_DYING;

	// Are we lifting prey?
	if ( GetEnemy() )
	{
		// Cleanup
		LostPrey( false );
	}
	else if ( m_bSwallowingPrey && m_hRagdoll )
	{
		// We're swallowing a body. Make it stick inside us.
		m_hTongueTip->SetAbsVelocity( vec3_origin );

		m_hRagdoll->StopFollowingEntity();
		m_hRagdoll->SetMoveType( MOVETYPE_VPHYSICS );
		m_hRagdoll->SetAbsOrigin( m_hTongueTip->GetAbsOrigin() );
		m_hRagdoll->RemoveSolidFlags( FSOLID_NOT_SOLID );
		m_hRagdoll->SetCollisionGroup( COLLISION_GROUP_DEBRIS ); 
		m_hRagdoll->RecheckCollisionFilter();
		if ( npc_barnacle_swallow.GetBool() )
		{
			m_hRagdoll->SetThink( NULL );
			m_hRagdoll->SetBlendWeight( 1.0f );
		}
	}
	else
	{
		// Destroy the ragdoll->tongue tip constraint
  		if ( m_pConstraint )
  		{
  			physenv->DestroyConstraint( m_pConstraint );
  			m_pConstraint = NULL;
  		}
		LostPrey( true );
	}

	// Puke gibs unless we're told to be cheap
	bool spawnGibs = ( !HasSpawnFlags( SF_BARNACLE_CHEAP_DEATH ) || random->RandomInt( 0, 1 ) );

	if ( spawnGibs )
	{
		SpawnDeathGibs();
	}

	// Puke blood
#ifdef _XBOX
	UTIL_BloodSpray( GetAbsOrigin(), Vector(0,0,-1), BLOOD_COLOR_YELLOW, 8, FX_BLOODSPRAY_ALL );
#else
	UTIL_BloodSpray( GetAbsOrigin(), Vector(0,0,-1), BLOOD_COLOR_RED, 8, FX_BLOODSPRAY_ALL );
#endif

	// Put blood on the ground if near enough
	trace_t bloodTrace;
	AI_TraceLine( GetAbsOrigin(), GetAbsOrigin() - Vector( 0, 0, 256 ), MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &bloodTrace);
	
	if ( bloodTrace.fraction < 1.0f )
	{
#ifdef _XBOX
		UTIL_BloodDecalTrace( &bloodTrace, BLOOD_COLOR_YELLOW );
#else
		UTIL_BloodDecalTrace( &bloodTrace, BLOOD_COLOR_RED );
#endif
	}

	EmitSound( "NPC_Barnacle.Die" );

	SetActivity( ACT_DIESIMPLE );

	StudioFrameAdvance();

	SetNextThink( gpGlobals->curtime + 0.1f );
	SetThink ( &CNPC_Barnacle::WaitTillDead );

	// we deliberately do not call BaseClass::EventKilled
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CNPC_Barnacle::WaitTillDead ( void )
{
	SetNextThink( gpGlobals->curtime + 0.1f );

	StudioFrameAdvance();
	DispatchAnimEvents ( this );

	if ( IsActivityFinished() )
	{
		// death anim finished. 
		StopAnimation();
	}

	float goalAltitude = BARNACLE_DEAD_TONGUE_ALTITUDE;

	trace_t tr;
	AI_TraceLine( m_vecRoot.Get(), m_vecRoot.Get() - Vector( 0, 0, 256 ), MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr );

	if ( tr.fraction < 1.0 )
	{
		float distToFloor = ( m_vecRoot.Get() - tr.endpos ).Length();
		float clearance = distToFloor - goalAltitude;

		if ( clearance < BARNACLE_MIN_DEAD_TONGUE_CLEARANCE )
		{
			if ( distToFloor - BARNACLE_MIN_DEAD_TONGUE_CLEARANCE > distToFloor * .5 )
			{
				goalAltitude = distToFloor - BARNACLE_MIN_DEAD_TONGUE_CLEARANCE;
			}
			else
			{
				goalAltitude = distToFloor * .5;
			}
		}
	}

	// Keep moving the tongue to its dead position
	// FIXME: This stupid algorithm is necessary because 
	// I can't seem to get reproduceable behavior from springs
	bool bTongueInPosition = false;
	float flDist = m_vecRoot.Get().z - m_vecTip.Get().z;
	if ( fabs(flDist - goalAltitude) > 20.0f )	
	{
		float flNewAltitude;
		float dt = gpGlobals->curtime - GetLastThink();
		if ( m_flAltitude >= goalAltitude )
		{
			flNewAltitude = max( goalAltitude, m_flAltitude - m_flBarnaclePullSpeed * dt );
		}
		else
		{
			flNewAltitude = min( goalAltitude, m_flAltitude + m_flBarnaclePullSpeed * dt );
		}
		SetAltitude( flNewAltitude );
	}
	else
	{
		// Wait for settling...
		IPhysicsObject *pTipObject = m_hTongueTip->VPhysicsGetObject();
		
		Vector vecVelocity;
		AngularImpulse angVel;
		pTipObject->GetVelocity( &vecVelocity, &angVel );
		if ( vecVelocity.LengthSqr() < 1.0f )
		{
			// We may need to have a heavier spring constant until we settle
			// to avoid strange looking rest conditions (when the tongue is really bent from
			// picking up a barrel, it looks strange to switch to the hanging constant)
			m_hTongueTip->m_pSpring->SetSpringConstant( BARNACLE_TONGUE_SPRING_CONSTANT_HANGING );
			if ( fabs(flDist - goalAltitude) > 1.0f )
			{
				float flSign = ( flDist > goalAltitude ) ? -1.0f : 1.0f;
				SetAltitude( m_flAltitude + flSign );
			}
			else if ( vecVelocity.LengthSqr() < 0.01f )
			{
				bTongueInPosition = ( fabs(flDist - goalAltitude) <= 1.0f );
			}
		}
	}

	if ( IsActivityFinished() && bTongueInPosition )
	{
		// Remove our tongue pieces
		UTIL_Remove( m_hTongueTip );
		UTIL_Remove( m_hTongueRoot );
		m_hTongueTip = NULL;
		m_hTongueRoot = NULL;

		SetThink ( NULL );
		m_lifeState	= LIFE_DEAD;
	}
	else
	{
		UpdateTongue();
	}
}

#if HL2_EPISODIC
//=========================================================
// Some creatures are poisonous to barnacles, and the barnacle
// will die after consuming them. This determines if a given 
// entity is one of those things.
// todo: could be a bit faster
//=========================================================
bool CNPC_Barnacle::IsPoisonous( CBaseEntity *pVictim )
{
	if (!pVictim)
		return false;

	if ( FClassnameIs(pVictim,"npc_headcrab_poison") )
		return true;

	if ( FClassnameIs(pVictim,"npc_headcrab_black") )
		return true;

	if ( FClassnameIs(pVictim,"npc_antlion") &&
		 static_cast<CNPC_Antlion *>(pVictim)->IsWorker()
		)
		return true;
	
	return false;
}




//=========================================================
// script input to immediately abandon whatever I am lifting
//=========================================================
void CNPC_Barnacle::InputLetGo( inputdata_t &inputdata )
{
	if ( GetEnemy() )
	{
		if ( !GetEnemy()->IsPlayer() )
		{
			// ignore the object so we don't get into a loop of trying to pick it up.
			m_hLastSpitEnemy = GetEnemy();
		}

		LostPrey( false );
	}
}


// Barnacle has custom impact damage tables, so it can take grave damage from sawblades.
static impactentry_t barnacleLinearTable[] =
{
	{ 150*150, 5 },
	{ 250*250, 10 },
	{ 350*350, 50 },
	{ 500*500, 100 },
	{ 1000*1000, 500 },
};


static impactentry_t barnacleAngularTable[] =
{
	{ 100*100, 35 },  // Sawblade always kills.
	{ 200*200, 50 },
	{ 250*250, 500 },
};

static impactdamagetable_t gBarnacleImpactDamageTable =
{
	barnacleLinearTable,
	barnacleAngularTable,

	ARRAYSIZE(barnacleLinearTable),
	ARRAYSIZE(barnacleAngularTable),

	24*24,		// minimum linear speed squared
	360*360,	// minimum angular speed squared (360 deg/s to cause spin/slice damage)
	2,			// can't take damage from anything under 2kg

	5,			// anything less than 5kg is "small"
	5,			// never take more than 5 pts of damage from anything under 5kg
	36*36,		// <5kg objects must go faster than 36 in/s to do damage

	VPHYSICS_LARGE_OBJECT_MASS,		// large mass in kg 
	4,			// large mass scale (anything over 500kg does 4X as much energy to read from damage table)
	5,			// large mass falling scale (emphasize falling/crushing damage over sideways impacts since the stress will kill you anyway)
	0.0f,		// min vel
};


const impactdamagetable_t &CNPC_Barnacle::GetPhysicsImpactDamageTable( void )
{
	return gBarnacleImpactDamageTable;
}

#endif


//=========================================================
// Precache - precaches all resources this monster needs
//=========================================================
void CNPC_Barnacle::Precache()
{
	PrecacheModel("models/barnacle.mdl");

	// Precache all gibs
	for ( size_t i=0; i < ARRAYSIZE(m_szGibNames); i++ )
	{
		PrecacheModel( m_szGibNames[i] );
	}

	PrecacheScriptSound( "NPC_Barnacle.Digest" );
	PrecacheScriptSound( "NPC_Barnacle.Scream" );
	PrecacheScriptSound( "NPC_Barnacle.PullPant" );
	PrecacheScriptSound( "NPC_Barnacle.TongueStretch" );
	PrecacheScriptSound( "NPC_Barnacle.FinalBite" );
	PrecacheScriptSound( "NPC_Barnacle.Die" );
	PrecacheScriptSound( "NPC_Barnacle.BreakNeck" );

	PrecacheModel( "models/props_junk/rock001a.mdl" );

	BaseClass::Precache();
}	

//=========================================================
// TongueTouchEnt - does a trace along the barnacle's tongue
// to see if any entity is touching it. Also stores the length
// of the trace in the int pointer provided.
//=========================================================
// enumerate entities that match a set of edict flags into a static array
class CTongueEntitiesEnum : public IPartitionEnumerator
{
public:
	CTongueEntitiesEnum( CBaseEntity **pList, int listMax );
	// This gets called	by the enumeration methods with each element
	// that passes the test.
	virtual IterationRetval_t EnumElement( IHandleEntity *pHandleEntity );
	
	int GetCount() { return m_nCount; }
	bool AddToList( CBaseEntity *pEntity );
	
private:
	CBaseEntity		**m_pList;
	int				m_nListMax;
	int				m_nCount;
};

CTongueEntitiesEnum::CTongueEntitiesEnum( CBaseEntity **pList, int listMax )
{
	m_pList = pList;
	m_nListMax = listMax;
	m_nCount = 0;
}

bool CTongueEntitiesEnum::AddToList( CBaseEntity *pEntity )
{
	m_pList[m_nCount] = pEntity;
	++m_nCount;
	return ( m_nCount < m_nListMax );
}

IterationRetval_t CTongueEntitiesEnum::EnumElement( IHandleEntity *pHandleEntity )
{
	CBaseEntity *pEntity = gEntList.GetBaseEntity( pHandleEntity->GetRefEHandle() );
	if ( pEntity )
	{
		if ( !AddToList( pEntity ) )
			return ITERATION_STOP;
	}
	return ITERATION_CONTINUE;
}


//-----------------------------------------------------------------------------
// Barnacle must trace against only brushes and its last enemy
//-----------------------------------------------------------------------------
class CBarnacleTongueFilter : public CTraceFilterSimple
{
	DECLARE_CLASS( CBarnacleTongueFilter, CTraceFilterSimple );

public:
	CBarnacleTongueFilter( CBaseEntity *pLastEnemy, const IHandleEntity *passedict, int collisionGroup ) : 
		CTraceFilterSimple( passedict, collisionGroup )
	{
		m_pLastEnemy = pLastEnemy;
		m_pBarnacle = const_cast<CBaseEntity*>( EntityFromEntityHandle( passedict ) );
	}

	virtual bool ShouldHitEntity( IHandleEntity *pServerEntity, int contentsMask )
	{
		if ( pServerEntity == m_pLastEnemy )
			return true;

#ifdef HL2_EPISODIC
		CBaseEntity *pEntity = EntityFromEntityHandle( pServerEntity );

		if ( pEntity )
		{
			if ( FStrEq( STRING( pEntity->m_iClassname ), "func_brush" ) )
			{
				CFuncBrush *pFuncBrush = assert_cast<CFuncBrush *>(pEntity);

				if ( pFuncBrush->m_bInvertExclusion )
				{
					if ( pFuncBrush->m_iszExcludedClass == m_pBarnacle->m_iClassname )
						return true;
					else
						return false;
				}
				else
				{
					if ( pFuncBrush->m_iszExcludedClass != m_pBarnacle->m_iClassname )
						return false;

				}
			}

			if ( pEntity->IsBSPModel() == false && pEntity->IsWorld() == false )
			{
				return false;
			}
		}
#endif

		return BaseClass::ShouldHitEntity( pServerEntity, contentsMask );
	}

private:
	CBaseEntity *m_pLastEnemy;
	CBaseEntity *m_pBarnacle;
};


#define BARNACLE_CHECK_SPACING	12
CBaseEntity *CNPC_Barnacle::TongueTouchEnt ( float *pflLength )
{
	trace_t		tr;
	float		length;

	int iMask = MASK_SOLID_BRUSHONLY;

#ifdef HL2_EPISODIC
	iMask = MASK_NPCSOLID;
#endif

	// trace once to hit architecture and see if the tongue needs to change position.
	CBarnacleTongueFilter tongueFilter( m_hLastSpitEnemy, this, COLLISION_GROUP_NONE );
	AI_TraceLine ( GetAbsOrigin(), GetAbsOrigin() - Vector ( 0 , 0 , 2048 ), 
		iMask, &tongueFilter, &tr );
	
	length = fabs( GetAbsOrigin().z - tr.endpos.z );
	// Pull it up a tad
	length = max(8, length - m_flRestUnitsAboveGround);
	if ( pflLength )
	{
		*pflLength = length;
	}

	Vector delta = Vector( BARNACLE_CHECK_SPACING, BARNACLE_CHECK_SPACING, 0 );
	Vector mins = GetAbsOrigin() - delta;
	Vector maxs = GetAbsOrigin() + delta;
	maxs.z = GetAbsOrigin().z;
	mins.z -= length;

	CBaseEntity *pList[10];
	CTongueEntitiesEnum tongueEnum( pList, 10 );
	partition->EnumerateElementsInBox( PARTITION_ENGINE_SOLID_EDICTS, mins, maxs, false, &tongueEnum );
	int nCount = tongueEnum.GetCount();
	if ( !nCount )
		return NULL;

	for ( int i = 0; i < nCount; i++ )
	{
		CBaseEntity *pTest = pList[i];

		// Can't lift something that's in the process of being lifted...
		// Necessary for good +use interactions
		if ( pTest->IsEFlagSet( EFL_IS_BEING_LIFTED_BY_BARNACLE ) )
			continue;

		// Vehicles can drive so fast that players can warp through the barnacle tongue.
		// Therefore, we have to do a check to ensure that doesn't happen.
		if ( pTest->GetServerVehicle() )
		{
			CBaseEntity *pDriver = pTest->GetServerVehicle()->GetPassenger();
			if ( pDriver )
			{
				Vector vecPrevDriverPos;
				pTest->GetVelocity( &vecPrevDriverPos );
				VectorMA( pDriver->GetAbsOrigin(), -0.1f, vecPrevDriverPos, vecPrevDriverPos );

				Ray_t sweptDriver;
				sweptDriver.Init( vecPrevDriverPos, pDriver->GetAbsOrigin(), pDriver->WorldAlignMins(), pDriver->WorldAlignMaxs() );
				if ( IsBoxIntersectingRay( mins, maxs, sweptDriver ) )
				{
					pTest = pDriver;
				}
			}
		}

		// Deal with physics objects
		if ( pTest->GetMoveType() == MOVETYPE_VPHYSICS )
		{
			IPhysicsObject *pObject = pTest->VPhysicsGetObject();
			if ( pObject && pObject->GetMass() <= BARNACLE_TONGUE_MAX_LIFT_MASS )
			{
				// If this is an item, make sure it's near the tongue before lifting it.
				// Weapons and other items have very large bounding boxes.
				if( pTest->GetSolidFlags() & FSOLID_TRIGGER )
				{
					if( UTIL_DistApprox2D( WorldSpaceCenter(), pTest->WorldSpaceCenter() ) > 16 )
					{
						continue;
					}
				}

				// Allow the barnacles to grab stuff while their tongue is lowering
#ifdef HL2_EPISODIC
				length = fabs( GetAbsOrigin().z - pTest->WorldSpaceCenter().z );
				// Pull it up a tad
				length = max(8, length - m_flRestUnitsAboveGround);
				if ( pflLength )
				{
					*pflLength = length;
				}
#endif

				return pTest;
			}
		}

		// NPCs + players
		CBaseCombatCharacter *pVictim = ToBaseCombatCharacter( pTest );
		if ( !pVictim )
			continue;

		// only clients and monsters
		if ( pTest != this && 
			 IRelationType( pTest ) == D_HT && 
			 pVictim->m_lifeState != LIFE_DEAD &&
			 pVictim->m_lifeState != LIFE_DYING &&
			 !( pVictim->GetFlags() & FL_NOTARGET )	)	
		{

			// Allow the barnacles to grab stuff while their tongue is lowering
#ifdef HL2_EPISODIC
			length = fabs( GetAbsOrigin().z - pTest->WorldSpaceCenter().z );
			// Pull it up a tad
			length = max(8, length - m_flRestUnitsAboveGround);
			if ( pflLength )
			{
				*pflLength = length;
			}
#endif

			return pTest;
		}
	}

	return NULL;
}

//===============================================================================================================================
// BARNACLE TONGUE TIP
//===============================================================================================================================
// Crane tip
LINK_ENTITY_TO_CLASS( npc_barnacle_tongue_tip, CBarnacleTongueTip );

BEGIN_DATADESC( CBarnacleTongueTip )

	DEFINE_FIELD( m_hBarnacle, FIELD_EHANDLE ),
	DEFINE_PHYSPTR( m_pSpring ),

END_DATADESC()

//-----------------------------------------------------------------------------
// Purpose: To by usable by vphysics, this needs to have a phys model.
//-----------------------------------------------------------------------------
void CBarnacleTongueTip::Spawn( void )
{
	Precache();
	SetModel( "models/props_junk/rock001a.mdl" );
	AddEffects( EF_NODRAW );

	// We don't want this to be solid, because we don't want it to collide with the barnacle.
	SetSolid( SOLID_VPHYSICS );
	AddSolidFlags( FSOLID_NOT_SOLID );
	BaseClass::Spawn();

	m_pSpring = NULL;
}

int CBarnacleTongueTip::UpdateTransmitState( void )
{
	return SetTransmitState( FL_EDICT_PVSCHECK );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CBarnacleTongueTip::Precache( void )
{
	BaseClass::Precache();
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CBarnacleTongueTip::UpdateOnRemove( )
{
	if ( m_pSpring )
	{
		physenv->DestroySpring( m_pSpring );
		m_pSpring = NULL;
	}
	BaseClass::UpdateOnRemove();
}


//-----------------------------------------------------------------------------
// If the tip changes, we gotta update the barnacle's notion of his tongue 
//-----------------------------------------------------------------------------
void CBarnacleTongueTip::VPhysicsUpdate( IPhysicsObject *pPhysics )
{
	BaseClass::VPhysicsUpdate( pPhysics );

	if ( m_hBarnacle.Get() )
	{
		m_hBarnacle->OnTongueTipUpdated();
	}
}


//-----------------------------------------------------------------------------
// Purpose: Activate/create the spring
//-----------------------------------------------------------------------------
bool CBarnacleTongueTip::CreateSpring( CBaseAnimating *pTongueRoot )
{
	IPhysicsObject *pPhysObject = VPhysicsGetObject();
	IPhysicsObject *pRootPhysObject = pTongueRoot->VPhysicsGetObject();
	Assert( pRootPhysObject );
	Assert( pPhysObject );

	// Root has huge mass, tip has little
	pRootPhysObject->SetMass( VPHYSICS_MAX_MASS );
	pPhysObject->SetMass( BARNACLE_TONGUE_TIP_MASS );
	float damping = 3;
	pPhysObject->SetDamping( &damping, &damping );

	springparams_t spring;
	spring.constant = BARNACLE_TONGUE_SPRING_CONSTANT_HANGING;
	spring.damping = BARNACLE_TONGUE_SPRING_DAMPING;
	spring.naturalLength = (GetAbsOrigin() - pTongueRoot->GetAbsOrigin()).Length();
	spring.relativeDamping = 10;
	spring.startPosition = GetAbsOrigin();
	spring.endPosition = pTongueRoot->GetAbsOrigin();
	spring.useLocalPositions = false;
	m_pSpring = physenv->CreateSpring( pPhysObject, pRootPhysObject, &spring );

	return true;
}


//-----------------------------------------------------------------------------
// Purpose: Create a barnacle tongue tip at the bottom of the tongue
//-----------------------------------------------------------------------------
CBarnacleTongueTip *CBarnacleTongueTip::CreateTongueTip( CNPC_Barnacle *pBarnacle, CBaseAnimating *pTongueRoot, const Vector &vecOrigin, const QAngle &vecAngles )
{
	CBarnacleTongueTip *pTip = (CBarnacleTongueTip *)CBaseEntity::Create( "npc_barnacle_tongue_tip", vecOrigin, vecAngles );
	if ( !pTip )
		return NULL;

	pTip->VPhysicsInitNormal( pTip->GetSolid(), pTip->GetSolidFlags(), false );
	if ( !pTip->CreateSpring( pTongueRoot ) )
		return NULL;

	// Set the backpointer to the barnacle
	pTip->m_hBarnacle = pBarnacle;

	// Don't collide with the world
	IPhysicsObject *pTipPhys = pTip->VPhysicsGetObject();

	// turn off all floating / fluid simulation
	pTipPhys->SetCallbackFlags( pTipPhys->GetCallbackFlags() & (~CALLBACK_DO_FLUID_SIMULATION) );
	
	return pTip;
}

//-----------------------------------------------------------------------------
// Purpose: Create a barnacle tongue tip at the root (i.e. inside the barnacle)
//-----------------------------------------------------------------------------
CBarnacleTongueTip *CBarnacleTongueTip::CreateTongueRoot( const Vector &vecOrigin, const QAngle &vecAngles )
{
	CBarnacleTongueTip *pTip = (CBarnacleTongueTip *)CBaseEntity::Create( "npc_barnacle_tongue_tip", vecOrigin, vecAngles );
	if ( !pTip )
		return NULL;

	pTip->AddSolidFlags( FSOLID_NOT_SOLID );

	// Disable movement on the root, we'll move this thing manually.
	pTip->VPhysicsInitShadow( false, false );
	pTip->SetMoveType( MOVETYPE_NONE );
	return pTip;
}

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

AI_BEGIN_CUSTOM_NPC( npc_barnacle, CNPC_Barnacle )

	// Register our interactions
	DECLARE_INTERACTION( g_interactionBarnacleVictimDangle )
	DECLARE_INTERACTION( g_interactionBarnacleVictimReleased )
	DECLARE_INTERACTION( g_interactionBarnacleVictimGrab )
	DECLARE_INTERACTION( g_interactionBarnacleVictimBite )

	// Conditions
		
	// Tasks

	// Activities
	DECLARE_ACTIVITY( ACT_BARNACLE_SLURP )			// Pulling the tongue up with prey on the end
	DECLARE_ACTIVITY( ACT_BARNACLE_BITE_HUMAN )		// Biting the head of a humanoid
	DECLARE_ACTIVITY( ACT_BARNACLE_BITE_PLAYER )	// Biting the head of a humanoid
	DECLARE_ACTIVITY( ACT_BARNACLE_CHEW_HUMAN )		// Slowly swallowing the humanoid
	DECLARE_ACTIVITY( ACT_BARNACLE_BARF_HUMAN )		// Spitting out human legs & gibs
	DECLARE_ACTIVITY( ACT_BARNACLE_TONGUE_WRAP )	// Wrapping the tongue around a target
	DECLARE_ACTIVITY( ACT_BARNACLE_TASTE_SPIT )		// Yuck! Me no like that!
	DECLARE_ACTIVITY( ACT_BARNACLE_BITE_SMALL_THINGS )	// Biting small things, like a headcrab
	DECLARE_ACTIVITY( ACT_BARNACLE_CHEW_SMALL_THINGS )	// Chewing small things, like a headcrab

	//Adrian: events go here
	DECLARE_ANIMEVENT( AE_BARNACLE_PUKEGIB )
	DECLARE_ANIMEVENT( AE_BARNACLE_BITE )
	DECLARE_ANIMEVENT( AE_BARNACLE_SPIT )
	// Schedules

AI_END_CUSTOM_NPC()
