Esempio n. 1
0
BOOL KBall::PassTo(KHero* pPasser, KHero* pReceiver)
{
    BOOL bResult = false;
    BOOL bRetCode = false;
    KPOSITION cSrcPos;
    KPOSITION cDstPos;
    KFix2PParabola cPath(GetCurrentGravity());
    int       nZMax = 0;

    KGLOG_PROCESS_ERROR(pPasser && pReceiver);
    KGLOG_PROCESS_ERROR(pPasser->m_dwID != pReceiver->m_dwID);

    bRetCode = IsTokenBy(pPasser);
    KG_PROCESS_ERROR(bRetCode);

    cSrcPos = GetPassSrcPos(pPasser, pReceiver);
    cDstPos = GetPassDstPos(pPasser, pReceiver);
    nZMax = GetPassZMax(cSrcPos, cDstPos);

    cPath.SetCtrlParams(cSrcPos, cDstPos, nZMax);
    cPath.Start(g_pSO3World->m_nGameLoop);

    DoTrackMove(&cPath);

    pPasser->ForbitAutoTakeBall(GAME_FPS);

    bResult = true;
Exit0:
    return bResult;
}
Esempio n. 2
0
void PhysicsLevelInit( void )
{
	physenv = physics->CreateEnvironment();
	assert( physenv );
#ifdef PORTAL
	physenv_main = physenv;
#endif
	{
	MEM_ALLOC_CREDIT();
	g_EntityCollisionHash = physics->CreateObjectPairHash();
	}

	// TODO: need to get the right factory function here
	//physenv->SetDebugOverlay( appSystemFactory );
	physenv->SetGravity( Vector(0, 0, -GetCurrentGravity() ) );
	// 15 ms per tick
	// NOTE: Always run client physics at this rate - helps keep ragdolls stable
	physenv->SetSimulationTimestep( IsXbox() ? DEFAULT_XBOX_CLIENT_VPHYSICS_TICK : DEFAULT_TICK_INTERVAL );
	physenv->SetCollisionEventHandler( &g_Collisions );
	physenv->SetCollisionSolver( &g_Collisions );

	g_PhysWorldObject = PhysCreateWorld_Shared( GetClientWorldEntity(), modelinfo->GetVCollide(1), g_PhysDefaultObjectParams );

	staticpropmgr->CreateVPhysicsRepresentations( physenv, &g_SolidSetup, NULL );
}
void KMovableObject::AfterProcessMoveZ()
{
    if (IsInAir())
    {
        // 悬空状态向下加速
        m_nVelocityZ -= GetCurrentGravity();
    }
}
IMotionEvent::simresult_e CPhysicsNPCSolver::Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject, 
													  float deltaTime, Vector &linear, AngularImpulse &angular )
{
	if ( IsIntersecting() )
	{
		const float PUSH_SPEED = 150.0f;

		if ( pObject->GetGameFlags() & FVPHYSICS_PLAYER_HELD )
		{
			CBasePlayer *pPlayer = UTIL_GetLocalPlayer();
			if ( pPlayer )
			{
				pPlayer->ForceDropOfCarriedPhysObjects( m_hEntity );
			}
		}

		ResetCancelTime();
		angular.Init();
		linear.Init();
		
		// Don't push on vehicles because they won't move
		if ( pObject->GetGameFlags() & FVPHYSICS_MULTIOBJECT_ENTITY )
		{
			if ( m_hEntity->GetServerVehicle() )
				return SIM_NOTHING;
		}

		Vector origin, vel;
		pObject->GetPosition( &origin, NULL );
		pObject->GetVelocity( &vel, NULL );
		Vector dir = origin - m_hNPC->GetAbsOrigin();
		dir.z = dir.z > 0 ? 0.1f : -0.1f;
		VectorNormalize(dir);
		AngularImpulse angVel;
		angVel.Init();

		// NOTE: Iterate this object's contact points 
		// if it can't move in this direction, try sliding along the plane/crease
		Vector pushImpulse;
		PhysComputeSlideDirection( pObject, dir * PUSH_SPEED, angVel, &pushImpulse, NULL, 0 );

		dir = pushImpulse;
		VectorNormalize(dir);

		if ( DotProduct( vel, dir ) < PUSH_SPEED * 0.5f )
		{
			linear = pushImpulse;
			if ( pObject->GetContactPoint(NULL,NULL) )
			{
				linear.z += GetCurrentGravity();
			}
		}
		return SIM_GLOBAL_ACCELERATION;
	}
	return SIM_NOTHING;
}
Esempio n. 5
0
BOOL KBall::BeShoot(KHero* pShooter, KBasketSocket* pTargetSocket, int nInterference)
{
    BOOL            bResult                 = false;
    BOOL            bRetCode                = false;
    int             nNowHitRate             = 0;
    int             nNowAttenuationTimes    = 0;
    KFix2PParabola  cPath(GetCurrentGravity());
    int             nZmax                   = 0;
    KPOSITION       cStart;
    KPOSITION       cEnd;

    KGLOG_PROCESS_ERROR(pShooter && pTargetSocket);

    bRetCode = CanBeShootBy(pShooter);
    KGLOG_PROCESS_ERROR(bRetCode);

    cStart = GetShootStartPos(pShooter);
    cEnd =  GetShootEndPos(pTargetSocket);

    pShooter->CalcHitRateParams(cStart, cEnd, nNowAttenuationTimes, nNowHitRate, nInterference);
    m_bCanHit = pShooter->RandomHitted(nNowHitRate);

    LogInfo(
        "Canhit:%d, hitrate:%d, virtualframe:%d, nInterference:%d", 
        m_bCanHit, nNowHitRate, pShooter->m_nVirtualFrame, nInterference
    );

    if (!m_bCanHit)
    {
        int nFlag = 1;
        if (nNowAttenuationTimes % 2 == 0)
            nFlag = -1;
        cEnd.nX += nFlag * pShooter->m_nMaxHitOffset * (HUNDRED_NUM - nNowHitRate) / HUNDRED_NUM;
        cEnd.nY += nFlag * pShooter->m_nMaxHitOffset * (HUNDRED_NUM - nNowHitRate) / HUNDRED_NUM;
    }

    nZmax = CalcProcessTrackMoveZmax(pShooter->m_nVirtualFrame, cStart, cEnd);

    cPath.SetCtrlParams(cStart, cEnd, nZmax);
    cPath.Start(g_pSO3World->m_nGameLoop);
    DoTrackMove(&cPath);

    m_nTotalFrame   = cPath.GetTotalFrame();
    m_pTargetSocket = pTargetSocket;
    m_dwShooterID   = pShooter->m_dwID;
    m_dwThrowerID   = ERROR_ID;

    bResult = true;
Exit0:
    return bResult;
}
void KMovableObject::ProcessXYRebound()
{
    m_nVelocityX = (int)(m_fXYReboundCoeX * m_nVelocityX);
    m_nVelocityY = (int)(m_fXYReboundCoeY * m_nVelocityY);

    m_nVelocityZ += GetCurrentGravity();
    m_nVelocityZ = -(int)(m_fXYReboundCoeZ * m_nVelocityZ);

    if (m_nVelocityZ < 0)
        m_nVelocityZ = 0;

    m_bXYRebound = false;
    //LogInfo("XY面碰撞后, V(%d, %d, %d), loop:%d", m_nVelocityX, m_nVelocityY, m_nVelocityZ, g_pSO3World->m_nGameLoop);
}
Esempio n. 7
0
BOOL KBall::SkillShoot(KHero* pShooter, KBasketSocket* pSocket)
{
    BOOL            bResult     = false;
    BOOL            bRetCode    = false;
    int             nVelocity   = CELL_LENGTH;
    KSkillShootPath cPath((GetCurrentGravity()));
    KPOSITION       cSocketPos;
    KPOSITION       cSrc;
    KPOSITION       cDst;
    KPOSITION       cCtrl;
    
    KGLOG_PROCESS_ERROR(pShooter && pSocket);

    bRetCode = CanBeShootBy(pShooter);
    KG_PROCESS_ERROR(bRetCode);

    cSocketPos = pSocket->GetPosition();

    cSrc = GetShootStartPos(pShooter);

    cDst = cSocketPos;
    cDst.nZ += CELL_LENGTH * 3;

    cCtrl.nX = (pSocket->m_eDir == csdLeft) ? cSocketPos.nX - (CELL_LENGTH * 12) : cSocketPos.nX + (CELL_LENGTH * 12);
    cCtrl.nY = cSocketPos.nY;
    cCtrl.nZ = CELL_LENGTH * 4;

    cPath.SetCtrlParams(cSrc, cDst, cCtrl, nVelocity);
    cPath.Start(g_pSO3World->m_nGameLoop);

    DoTrackMove(&cPath);

    m_bAttackState = true;
    m_dwShooterID = pShooter->m_dwID;
    m_dwThrowerID = ERROR_ID;
    m_pTargetSocket = pSocket;
    m_bRestAfterTrackMove = true;

    bResult = true;
Exit0:
    return bResult;
}
Esempio n. 8
0
//------------------------------------------------------------------------------
// Apply the forces to the entity
//------------------------------------------------------------------------------
IMotionEvent::simresult_e C_EntityDissolve::Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject, float deltaTime, Vector &linear, AngularImpulse &angular )
{
	linear.Init();
	angular.Init();

	// Make it zero g
	linear.z -= -1.02 * GetCurrentGravity();

	Vector vel;
	AngularImpulse angVel;
	pObject->GetVelocity( &vel, &angVel );
	vel += linear * deltaTime; // account for gravity scale

	Vector unitVel = vel;
	Vector unitAngVel = angVel;

	float speed = VectorNormalize( unitVel );
//	float angSpeed = VectorNormalize( unitAngVel );

//	float speedScale = 0.0;
//	float angSpeedScale = 0.0;

	float flLinearLimit = 50;
	float flLinearLimitDelta = 40;
	if ( speed > flLinearLimit )
	{
		float flDeltaVel = (flLinearLimit - speed) / deltaTime;
		if ( flLinearLimitDelta != 0.0f )
		{
			float flMaxDeltaVel = -flLinearLimitDelta / deltaTime;
			if ( flDeltaVel < flMaxDeltaVel )
			{
				flDeltaVel = flMaxDeltaVel;
			}
		}
		VectorMA( linear, flDeltaVel, unitVel, linear );
	}

	return SIM_GLOBAL_ACCELERATION;
}
Esempio n. 9
0
BOOL KBall::SkillSlamEx(KHero* pSlamer, KSlamBallSkill* pSkill, KBasketSocket* pTargetSocket)
{
    BOOL            bResult                 = false;
    BOOL            bRetCode                = false;
    int             nNowHitRate             = 0;
    int             nNowAttenuationTimes    = 0;
    int             nZmax                   = 0;
    KFix2PParabola  cPath(GetCurrentGravity());
    KPOSITION       cSrc;
    KPOSITION       cDst;
    int             nTotalFrame             = 0;

    KG_PROCESS_ERROR(pSlamer && pTargetSocket);

    bRetCode = CanSkillSlam(pSlamer, pTargetSocket);
    KGLOG_PROCESS_ERROR(bRetCode);

    cSrc = GetShootStartPos(pSlamer);
    cDst = GetSkillSlamDstPos(pSlamer, pTargetSocket);

    nZmax = CalcProcessTrackMoveZmax(pSlamer->m_nVirtualFrame, cSrc, cDst);

    cPath.SetCtrlParams(cSrc, cDst, nZmax);
    cPath.Start(pSlamer->m_nVirtualFrame);
    pSlamer->DoTrackMove(&cPath);
    nTotalFrame = cPath.GetTotalFrame();
    pSlamer->LoseControlByCounter(nTotalFrame);

    m_bCanHit = true;
    m_nTotalFrame = cPath.GetTotalFrame();
    m_pTargetSocket = pTargetSocket;
    m_dwShooterID = pSlamer->m_dwID;
    m_dwThrowerID = ERROR_ID;

    bResult = true;
Exit0:
    return bResult;
}
//-----------------------------------------------------------------------------
// Purpose: Hop off the ground to start deployment
//-----------------------------------------------------------------------------
void CGrenadeHopwire::Detonate( void )
{
	SetModel( GRENADE_MODEL_OPEN );

	AngularImpulse	hopAngle = RandomAngularImpulse( -300, 300 );

	//Find out how tall the ceiling is and always try to hop halfway
	trace_t	tr;
	UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + Vector( 0, 0, MAX_HOP_HEIGHT*2 ), MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );

	// Jump half the height to the found ceiling
	float hopHeight = MIN( MAX_HOP_HEIGHT, (MAX_HOP_HEIGHT*tr.fraction) );

	//Add upwards velocity for the "hop"
	Vector hopVel( 0.0f, 0.0f, hopHeight );
	SetVelocity( hopVel, hopAngle );

	// Get the time until the apex of the hop
	float apexTime = sqrt( hopHeight / GetCurrentGravity() );

	// Explode at the apex
	SetThink( &CGrenadeHopwire::CombatThink );
	SetNextThink( gpGlobals->curtime + apexTime);
}
Esempio n. 11
0
void CWeaponBugBait::DrawArc(  )
{
	int idxImpact = NUM_ARC_POINTS-1;
	float curtime = gpGlobals->curtime;
	CBaseCombatCharacter *pOwner  = GetOwner();
	
	if ( pOwner == NULL )
		return;

	CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
	
	if ( pPlayer == NULL )
		return;

	Vector throwVelocity, shootDirection;
	Vector origin = pPlayer->EyePosition() + pPlayer->EyeToWeaponOffset();
	pPlayer->EyeVectors(&shootDirection);

	pPlayer->GetVelocity( &throwVelocity, NULL );
	throwVelocity +=  shootDirection * 1000;
	throwVelocity *= ARC_TIME_UNIT;  

	Vector vecGravity = Vector(0,0,-GetCurrentGravity() * ARC_TIME_UNIT * ARC_TIME_UNIT);
	
	Vector last = origin;
	bool impacted = false;

	// Reset the indicator pulse time
	float pulseSpeedScale = 1;
	if ( curtime > m_pulseStart + NUM_ARC_POINTS*ARC_TIME_UNIT*pulseSpeedScale*2.5)
		m_pulseStart = curtime;
	
	for ( int i = 0; i < NUM_ARC_POINTS - 1; i ++ )
	{
		if ( impacted )
		{
		 	m_hArcPoints[i]->TurnOff();
			continue;
		}

		// Position at a specific point in time:
		// p(n) = orig + n*vel + ((n^2+n)*accel) / 2
		int t = i+1;
		Vector position = origin + throwVelocity*t + ((t*t+t)*vecGravity) / 2;  
		
		
		m_hArcPoints[i]->TurnOn();
		m_hArcPoints[i]->SetAbsOrigin(position);
		float pct = i/(float) NUM_ARC_POINTS;
		m_hArcPoints[i]->SetBrightness( 80 - 80*pct*pct );
		
		// show a segment brighter if it fits within the pulse window
		float elapsed = curtime-m_pulseStart; 
		int pulseSegment = floor(elapsed / (ARC_TIME_UNIT*pulseSpeedScale));
		
		if ( i == pulseSegment )
			m_hArcPoints[i]->SetScale(ARC_SPRITE_SCALE*2, .2);
		else 
			m_hArcPoints[i]->SetScale(ARC_SPRITE_SCALE, .2);
		
		trace_t tr;
		UTIL_TraceLine(last, position, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );

		if ( tr.fraction < 1 ) // segment impacted, place impact sprite
		{
			impacted = true;

			m_hArcPoints[i]->TurnOff();
			m_hArcPoints[idxImpact]->SetAbsOrigin(tr.endpos);
			m_hArcPoints[idxImpact]->TurnOn();
		}

		last = position;
	}

	if ( !impacted )
	{
		m_hArcPoints[idxImpact]->TurnOff();
	}
}
Esempio n. 12
0
//=========================================================
// HandleAnimEvent - catches the monster-specific messages
// that occur when tagged animation frames are played.
//=========================================================
void CNPC_Houndeye::HandleAnimEvent( animevent_t *pEvent )
{
	switch ( pEvent->event )
	{
		case HOUND_AE_WARN:
			// do stuff for this event.
			WarnSound();
			break;

		case HOUND_AE_STARTATTACK:
			WarmUpSound();
			break;

		case HOUND_AE_HOPBACK:
			{
				float flGravity = GetCurrentGravity();

				SetGroundEntity( NULL );

				Vector forward;
				AngleVectors( GetLocalAngles(), &forward );
				Vector vecNewVelocity = forward * -200;
				//jump up 36 inches
				vecNewVelocity.z += sqrt( 2 * flGravity * 36 );
				SetAbsVelocity( vecNewVelocity );
				break;
			}

		case HOUND_AE_THUMP:
			// emit the shockwaves
			SonicAttack();
			m_flNextAttack = gpGlobals->curtime + random->RandomFloat( 5.0, 8.0 );
			break;

		case HOUND_AE_ANGERSOUND1:
			{
				EmitSound( "NPC_Houndeye.Anger1" );
			}
			break;

		case HOUND_AE_ANGERSOUND2:
			{
			EmitSound( "NPC_Houndeye.Anger2" );
			}
			break;

		case HOUND_AE_CLOSE_EYE:
			if ( !m_fDontBlink )
			{
			//<<TEMP>>	pev->skin = HOUNDEYE_EYE_FRAMES - 1;
			}
			break;

		case HOUND_AE_LEAP_HIT:
			{
				//<<TEMP>>return;//<<TEMP>>
				SetGroundEntity( NULL );

				//
				// Take him off ground so engine doesn't instantly reset FL_ONGROUND.
				//
				UTIL_SetOrigin( this, GetLocalOrigin() + Vector( 0 , 0 , 1 ));
				Vector vecJumpDir;
				if ( GetEnemy() != NULL )
				{
					Vector vecEnemyEyePos = GetEnemy()->EyePosition();

					float gravity = GetCurrentGravity();
					if ( gravity <= 1 )
					{
						gravity = 1;
					}

					//
					// How fast does the houndeye need to travel to reach my enemy's eyes given gravity?
					//
					float height = ( vecEnemyEyePos.z - GetAbsOrigin().z );
					if ( height < 16 )
					{
						height = 16;
					}
					else if ( height > 120 )
					{
						height = 120;
					}
					float speed = sqrt( 2 * gravity * height );
					float time = speed / gravity;

					//
					// Scale the sideways velocity to get there at the right time
					//
					vecJumpDir = vecEnemyEyePos - GetAbsOrigin();
					vecJumpDir = vecJumpDir / time;

					//
					// Speed to offset gravity at the desired height.
					//
					vecJumpDir.z = speed;

					//
					// Don't jump too far/fast.
					//
					float distance = vecJumpDir.Length();
					if ( distance > 650 )
					{
						vecJumpDir = vecJumpDir * ( 650.0 / distance );
					}
				}
				else
				{
					Vector forward, up;
					AngleVectors( GetLocalAngles(), &forward, NULL, &up );
					//
					// Jump hop, don't care where.
					//
					vecJumpDir = Vector( forward.x, forward.y, up.z ) * 350;
				}

				SetAbsVelocity( vecJumpDir );
				m_flNextAttack = gpGlobals->curtime + 2;
				break;
			}
		default:
			BaseClass::HandleAnimEvent( pEvent );
			break;
	}
}
Esempio n. 13
0
//-----------------------------------------------------------------------------
// Purpose: Override to check throw
// Input  :
// Output :
//-----------------------------------------------------------------------------
int CWeaponMolotov::WeaponRangeAttack1Condition( float flDot, float flDist )
{
	// If things haven't changed too much since last time
	// just return that previously calculated value
	if (gpGlobals->curtime < m_fNextThrowCheck )
	{
		return m_iThrowBits;
	}
	
	if ( flDist < m_fMinRange1) {
		m_iThrowBits = COND_TOO_CLOSE_TO_ATTACK;
	}
	else if (flDist > m_fMaxRange1) {
		m_iThrowBits = COND_TOO_FAR_TO_ATTACK;
	}
	else if (flDot < 0.5) {
		m_iThrowBits = COND_NOT_FACING_ATTACK;
	}

	// If moving, can't throw.
	else if ( m_flGroundSpeed != 0 )
	{
		m_iThrowBits = COND_NONE;
	}
	else {
		// Ok we should check again as some time has passed
		// This function is only used by NPC's so we can cast to a Base Monster
		CAI_BaseNPC *pNPC	= GetOwner()->MyNPCPointer();
		CBaseEntity *pEnemy = pNPC->GetEnemy();

		if (!pEnemy)
		{
			m_iThrowBits = COND_NONE;
		}
		// Get Enemy Position 
		Vector vecTarget;
		pEnemy->CollisionProp()->NormalizedToWorldSpace( Vector( 0.5f, 0.5f, 0.0f ), &vecTarget );

		// Get Toss Vector
		Vector			throwStart  = pNPC->Weapon_ShootPosition();
		Vector			vecToss;
		CBaseEntity*	pBlocker	= NULL;
		float			throwDist	= (throwStart - vecTarget).Length();
		float			fGravity	= GetCurrentGravity();
		float			throwLimit	= pNPC->ThrowLimit(throwStart, vecTarget, fGravity, 35, WorldAlignMins(), WorldAlignMaxs(), pEnemy, &vecToss, &pBlocker);

		// If I can make the throw (or most of the throw)
		if (!throwLimit || (throwLimit != throwDist && throwLimit > 0.8*throwDist))
		{
			m_vecTossVelocity = vecToss;
			m_iThrowBits = COND_CAN_RANGE_ATTACK1;

		}
		else
		{
			m_iThrowBits = COND_NONE;
		}

	}
	// don't check again for a while.
	m_fNextThrowCheck = gpGlobals->curtime + 0.33; // 1/3 second.

	return m_iThrowBits;
}
Esempio n. 14
0
//------------------------------------------------------------------------------
// Purpose : Move toward targetmap 
// Input   :
// Output  :
//------------------------------------------------------------------------------
void CGrenadeHomer::AimThink( void )
{ 
	// Blow up the missile if we have an explicit detonate time that
	// has been reached
	if (m_flDetonateTime != 0 &&
		gpGlobals->curtime > m_flDetonateTime)
	{
		Detonate();
		return;
	}

	PlayFlySound();

	Vector		vTargetPos	= vec3_origin;
	Vector		vTargetDir;
	float		flCurHomingStrength = 0;

	// ------------------------------------------------
	//  If I'm homing
	// ------------------------------------------------
	if (m_hTarget != NULL)
	{
		vTargetPos		= m_hTarget->EyePosition();
		vTargetDir		= vTargetPos - GetAbsOrigin();
		VectorNormalize(vTargetDir);

		// --------------------------------------------------
		//  If my target is far away do some primitive
		//  obstacle avoidance
		// --------------------------------------------------
		if ((vTargetPos - GetAbsOrigin()).Length() > 200)
		{
			Vector  vTravelDir	= GetAbsVelocity();
			VectorNormalize(vTravelDir);
			vTravelDir *= 50;

			trace_t tr;
			UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + vTravelDir, MASK_SHOT, m_hTarget, COLLISION_GROUP_NONE, &tr );
			if (tr.fraction != 1.0)
			{
				// Head off in normal 
				float dotPr			=  DotProduct(vTravelDir,tr.plane.normal);
				Vector vBounce		=  -dotPr * tr.plane.normal;
				vBounce.z			=  0;
				VectorNormalize(vBounce);
				vTargetDir			+= vBounce;
				VectorNormalize(vTargetDir);
				// DEBUG TOOL
				//NDebugOverlay::Line(GetOrigin(), GetOrigin()+vTravelDir, 255,0,0, true, 20);
				//NDebugOverlay::Line(GetOrigin(), GetOrigin()+(12*tr.plane.normal), 0,0,255, true, 20);
				//NDebugOverlay::Line(GetOrigin(), GetOrigin()+(vTargetDir), 0,255,0, true, 20);
			}
		}

		float	flTargetSpeed					= GetAbsVelocity().Length();
		float	flHomingRampUpStartTime			= m_flHomingLaunchTime		+ m_flHomingDelay;
		float	flHomingSustainStartTime		= flHomingRampUpStartTime	+ m_flHomingRampUp;
		float	flHomingRampDownStartTime		= flHomingSustainStartTime	+ m_flHomingDuration;
		float	flHomingEndHomingTime			= flHomingRampDownStartTime + m_flHomingRampDown;
		// ---------
		// Delay
		// ---------
		if		(gpGlobals->curtime < flHomingRampUpStartTime)
		{
			flCurHomingStrength = 0;
			flTargetSpeed		= 0;
		}
		// ----------
		//  Ramp Up
		// ----------
		else if (gpGlobals->curtime < flHomingSustainStartTime)
		{
			float flAge			= gpGlobals->curtime - flHomingRampUpStartTime;
			flCurHomingStrength = m_flHomingStrength * (flAge/m_flHomingRampUp);
			flTargetSpeed		= flCurHomingStrength * m_flHomingSpeed;
		}
		// ----------
		//  Sustain
		// ----------
		else if (gpGlobals->curtime < flHomingRampDownStartTime)
		{
			flCurHomingStrength = m_flHomingStrength;
			flTargetSpeed		= m_flHomingSpeed;
		}
		// -----------
		//  Ramp Down
		// -----------
		else if (gpGlobals->curtime < flHomingEndHomingTime)
		{
			float flAge			= gpGlobals->curtime - flHomingRampDownStartTime;
			flCurHomingStrength = m_flHomingStrength * (1-(flAge/m_flHomingRampDown));
			flTargetSpeed		= m_flHomingSpeed;
		}
		// ---------------
		//  Set Homing
		// ---------------
		if (flCurHomingStrength > 0)
		{	
			// -------------
			// Smoke trail.
			// -------------
			if (m_nRocketTrailType == HOMER_SMOKE_TRAIL_ON_HOMING)
			{
				UpdateRocketTrail(flCurHomingStrength);
			}

			// Extract speed and direction
			Vector	vCurDir		= GetAbsVelocity();
			float flCurSpeed = VectorNormalize(vCurDir);
			flTargetSpeed = MAX(flTargetSpeed, flCurSpeed);

			// Add in homing direction
			Vector vecNewVelocity = GetAbsVelocity();
			float flTimeToUse = gpGlobals->frametime;
			while (flTimeToUse > 0)
			{
				vecNewVelocity = (flCurHomingStrength * vTargetDir) + ((1 - flCurHomingStrength) * vCurDir);
				flTimeToUse = -0.1;
			}
			VectorNormalize(vecNewVelocity);
			vecNewVelocity *= flTargetSpeed;
			SetAbsVelocity( vecNewVelocity );
		}
	}
	
	// ----------------------------------------------------------------------------------------
	// Add time-coherent noise to the current velocity 
	// ----------------------------------------------------------------------------------------
	Vector vecImpulse( 0, 0, 0 );
	if (m_flSpinMagnitude > 0)
	{
		vecImpulse.x += m_flSpinMagnitude*sin(m_flSpinSpeed * gpGlobals->curtime + m_flSpinOffset);
		vecImpulse.y += m_flSpinMagnitude*cos(m_flSpinSpeed * gpGlobals->curtime + m_flSpinOffset);
		vecImpulse.z -= m_flSpinMagnitude*cos(m_flSpinSpeed * gpGlobals->curtime + m_flSpinOffset);
	}

	// Add in gravity
	vecImpulse.z -= GetGravity() * GetCurrentGravity() * gpGlobals->frametime;
	ApplyAbsVelocityImpulse( vecImpulse );

	QAngle angles;
	VectorAngles( GetAbsVelocity(), angles );
	SetLocalAngles( angles );

#if 0 // BUBBLE
	if( gpGlobals->curtime > m_flNextWarnTime )
	{
		// Make a bubble of warning sound in front of me.
		const float WARN_INTERVAL = 0.25f;
		float flSpeed = GetAbsVelocity().Length();
		Vector vecWarnLocation;

		// warn a little bit ahead of us, please.
		vecWarnLocation = GetAbsOrigin() + GetAbsVelocity() * 0.75;

		// Make a bubble of warning ahead of the missile.
		CSoundEnt::InsertSound ( SOUND_DANGER, vecWarnLocation, flSpeed * WARN_INTERVAL, 0.5 );

#if 0
		Vector vecRight, vecForward;

		AngleVectors( GetAbsAngles(), &vecForward, &vecRight, NULL );

		NDebugOverlay::Line( vecWarnLocation, vecWarnLocation + vecForward * flSpeed * WARN_INTERVAL * 0.5, 255,255,0, true, 10);
		NDebugOverlay::Line( vecWarnLocation, vecWarnLocation - vecForward * flSpeed * WARN_INTERVAL * 0.5, 255,255,0, true, 10);

		NDebugOverlay::Line( vecWarnLocation, vecWarnLocation + vecRight * flSpeed * WARN_INTERVAL * 0.5, 255,255,0, true, 10);
		NDebugOverlay::Line( vecWarnLocation, vecWarnLocation - vecRight * flSpeed * WARN_INTERVAL * 0.5, 255,255,0, true, 10);
#endif
		m_flNextWarnTime = gpGlobals->curtime + WARN_INTERVAL;
	}
#endif // BUBBLE

	SetNextThink( gpGlobals->curtime + 0.1f );
}
//=========================================================
// HandleAnimEvent - catches the monster-specific messages
// that occur when tagged animation frames are played.
//=========================================================
void CNPC_Bullsquid::HandleAnimEvent( animevent_t *pEvent )
{
	switch( pEvent->event )
	{
		case BSQUID_AE_SPIT:
		{
			Vector vSpitPos;
			GetAttachment("mouth", vSpitPos);
			vSpitPos.z += 40.0f;
			Vector	vTarget;

			// If our enemy is looking at us and far enough away, lead him
			if (HasCondition(COND_ENEMY_FACING_ME) && UTIL_DistApprox(GetAbsOrigin(), GetEnemy()->GetAbsOrigin()) > (40 * 12))
			{
				UTIL_PredictedPosition(GetEnemy(), 0.5f, &vTarget);
				vTarget.z = GetEnemy()->GetAbsOrigin().z;
			}
			else
			{
				// Otherwise he can't see us and he won't be able to dodge
				vTarget = GetEnemy()->BodyTarget(vSpitPos, true);
			}

			vTarget[2] += random->RandomFloat(0.0f, 32.0f);

			// Try and spit at our target
			Vector vecToss;
			if (GetSpitVector(vSpitPos, vTarget, &vecToss) == false)
			{
				DevMsg("GetSpitVector( vSpitPos, vTarget, &vecToss ) == false\n");
				// Now try where they were
				if (GetSpitVector(vSpitPos, m_vSavePosition, &vecToss) == false)
				{
					DevMsg("GetSpitVector( vSpitPos, m_vSavePosition, &vecToss ) == false\n");
					// Failing that, just shoot with the old velocity we calculated initially!
					vecToss = m_vecSaveSpitVelocity;
				}
			}

			// Find what our vertical theta is to estimate the time we'll impact the ground
			Vector vecToTarget = (vTarget - vSpitPos);
			VectorNormalize(vecToTarget);
			float flVelocity = VectorNormalize(vecToss);
			float flCosTheta = DotProduct(vecToTarget, vecToss);
			float flTime = (vSpitPos - vTarget).Length2D() / (flVelocity * flCosTheta);

			// Emit a sound where this is going to hit so that targets get a chance to act correctly
			CSoundEnt::InsertSound(SOUND_DANGER, vTarget, (15 * 12), flTime, this);

			// Don't fire again until this volley would have hit the ground (with some lag behind it)
			SetNextAttack(gpGlobals->curtime + flTime + random->RandomFloat(0.5f, 2.0f));

			for (int i = 0; i < 6; i++)
			{
				CGrenadeSpit *pGrenade = (CGrenadeSpit*)CreateEntityByName("grenade_spit");
				pGrenade->SetAbsOrigin(vSpitPos);
				pGrenade->SetAbsAngles(vec3_angle);
				DispatchSpawn(pGrenade);
				pGrenade->SetThrower(this);
				pGrenade->SetOwnerEntity(this);

				if (i == 0)
				{
					pGrenade->SetSpitSize(SPIT_LARGE);
					pGrenade->SetAbsVelocity(vecToss * flVelocity);
				}
				else
				{
					pGrenade->SetAbsVelocity((vecToss + RandomVector(-0.035f, 0.035f)) * flVelocity);
					pGrenade->SetSpitSize(random->RandomInt(SPIT_SMALL, SPIT_MEDIUM));
				}

				// Tumble through the air
				pGrenade->SetLocalAngularVelocity(QAngle(random->RandomFloat(-250, -500),
					random->RandomFloat(-250, -500),
					random->RandomFloat(-250, -500)));

			}

			for (int i = 0; i < 8; i++)
			{
				DispatchParticleEffect("blood_impact_yellow_01", vSpitPos + RandomVector(-12.0f, 12.0f), RandomAngle(0, 360));
			}

			EmitSound("NPC_Antlion.PoisonShoot");
		}
		break;

		case BSQUID_AE_BITE:
		{
		// SOUND HERE!
			CBaseEntity *pHurt = CheckTraceHullAttack( 70, Vector(-16,-16,-16), Vector(16,16,16), sk_bullsquid_dmg_bite.GetFloat(), DMG_SLASH );
			if ( pHurt )
			{
				Vector forward, up;
				AngleVectors( GetAbsAngles(), &forward, NULL, &up );
				pHurt->ApplyAbsVelocityImpulse( 100 * (up-forward) );
				pHurt->SetGroundEntity( NULL );
			}
		}
		break;

		case BSQUID_AE_WHIP_SND:
		{
			EmitSound( "NPC_Bullsquid.TailWhip" );
			break;
		}

/*
		case BSQUID_AE_TAILWHIP:
		{
			CBaseEntity *pHurt = CheckTraceHullAttack( 70, Vector(-16,-16,-16), Vector(16,16,16), sk_bullsquid_dmg_whip.GetFloat(), DMG_SLASH | DMG_ALWAYSGIB );
			if ( pHurt ) 
			{
				Vector right, up;
				AngleVectors( GetAbsAngles(), NULL, &right, &up );

				if ( pHurt->GetFlags() & ( FL_NPC | FL_CLIENT ) )
					 pHurt->ViewPunch( QAngle( 20, 0, -20 ) );
			
				pHurt->ApplyAbsVelocityImpulse( 100 * (up+2*right) );
			}
		}
		break;
*/

		case BSQUID_AE_BLINK:
		{
			// close eye. 
			m_nSkin = 1;
		}
		break;

		case BSQUID_AE_HOP:
		{
			float flGravity = GetCurrentGravity();

			// throw the squid up into the air on this frame.
			if ( GetFlags() & FL_ONGROUND )
			{
				SetGroundEntity( NULL );
			}

			// jump 40 inches into the air
			Vector vecVel = GetAbsVelocity();
			vecVel.z += sqrt( flGravity * 2.0 * 40 );
			SetAbsVelocity( vecVel );
		}
		break;

		case BSQUID_AE_THROW:
			{
				// squid throws its prey IF the prey is a client. 
				CBaseEntity *pHurt = CheckTraceHullAttack( 70, Vector(-16,-16,-16), Vector(16,16,16), 0, 0 );


				if ( pHurt )
				{
					pHurt->ViewPunch( QAngle(20,0,-20) );
							
					// screeshake transforms the viewmodel as well as the viewangle. No problems with seeing the ends of the viewmodels.
					UTIL_ScreenShake( pHurt->GetAbsOrigin(), 25.0, 1.5, 0.7, 2, SHAKE_START );

					// If the player, throw him around
					if ( pHurt->IsPlayer())
					{
						Vector forward, up;
						AngleVectors( GetLocalAngles(), &forward, NULL, &up );
						pHurt->ApplyAbsVelocityImpulse( forward * 300 + up * 300 );
					}
					// If not the player see if has bullsquid throw interatcion
					else
					{
						CBaseCombatCharacter *pVictim = ToBaseCombatCharacter( pHurt );
						if (pVictim)
						{
							if ( pVictim->DispatchInteraction( g_interactionBullsquidThrow, NULL, this ) )
							{
								Vector forward, up;
								AngleVectors( GetLocalAngles(), &forward, NULL, &up );
								pVictim->ApplyAbsVelocityImpulse( forward * 300 + up * 250 );
							}
						}
					}
				}
			}
		break;

		default:
			BaseClass::HandleAnimEvent( pEvent );
	}
}
Vector BullsquidVecCheckThrowTolerance(CBaseEntity *pEdict, const Vector &vecSpot1, Vector vecSpot2, float flSpeed, float flTolerance)
{
	flSpeed = MAX(1.0f, flSpeed);

	float flGravity = GetCurrentGravity();

	Vector vecGrenadeVel = (vecSpot2 - vecSpot1);

	// throw at a constant time
	float time = vecGrenadeVel.Length() / flSpeed;
	vecGrenadeVel = vecGrenadeVel * (1.0 / time);

	// adjust upward toss to compensate for gravity loss
	vecGrenadeVel.z += flGravity * time * 0.5;

	Vector vecApex = vecSpot1 + (vecSpot2 - vecSpot1) * 0.5;
	vecApex.z += 0.5 * flGravity * (time * 0.5) * (time * 0.5);

	trace_t tr;
	UTIL_TraceLine(vecSpot1, vecApex, MASK_SOLID, pEdict, COLLISION_GROUP_NONE, &tr);
	if (tr.fraction != 1.0)
	{
		// fail!
		if (g_debug_bullsquid.GetBool())
		{
			NDebugOverlay::Line(vecSpot1, vecApex, 255, 0, 0, true, 5.0);
		}

		return vec3_origin;
	}

	if (g_debug_bullsquid.GetBool())
	{
		NDebugOverlay::Line(vecSpot1, vecApex, 0, 255, 0, true, 5.0);
	}

	UTIL_TraceLine(vecApex, vecSpot2, MASK_SOLID_BRUSHONLY, pEdict, COLLISION_GROUP_NONE, &tr);
	if (tr.fraction != 1.0)
	{
		bool bFail = true;

		// Didn't make it all the way there, but check if we're within our tolerance range
		if (flTolerance > 0.0f)
		{
			float flNearness = (tr.endpos - vecSpot2).LengthSqr();
			if (flNearness < Square(flTolerance))
			{
				if (g_debug_bullsquid.GetBool())
				{
					NDebugOverlay::Sphere(tr.endpos, vec3_angle, flTolerance, 0, 255, 0, 0, true, 5.0);
				}

				bFail = false;
			}
		}

		if (bFail)
		{
			if (g_debug_bullsquid.GetBool())
			{
				NDebugOverlay::Line(vecApex, vecSpot2, 255, 0, 0, true, 5.0);
				NDebugOverlay::Sphere(tr.endpos, vec3_angle, flTolerance, 255, 0, 0, 0, true, 5.0);
			}
			return vec3_origin;
		}
	}

	if (g_debug_bullsquid.GetBool())
	{
		NDebugOverlay::Line(vecApex, vecSpot2, 0, 255, 0, true, 5.0);
	}

	return vecGrenadeVel;
}