void CPlayerPickupController::Init( CBasePlayer *pPlayer, CBaseEntity *pObject )
{
	m_pPlayer = pPlayer;

	IPhysicsObject *pPhysics = pObject->VPhysicsGetObject();
	Vector position;
	QAngle angles;
	pPhysics->GetPosition( &position, &angles );
	m_grabController.SetMaxImpulse( Vector(20*100,20*100,20*100), AngularImpulse(20*180,20*180,20*180) );
	m_grabController.AttachEntity( pObject, pPhysics, position, angles );
	// Holster player's weapon
	if ( m_pPlayer->GetActiveWeapon() )
	{
		if ( !m_pPlayer->GetActiveWeapon()->Holster() )
		{
			Shutdown();
			return;
		}
	}

	m_pPlayer->m_Local.m_iHideHUD |= HIDEHUD_WEAPONS;
	m_pPlayer->SetUseEntity( this );
	matrix3x4_t tmp;
	ComputePlayerMatrix( tmp );
	VectorITransform( position, tmp, m_positionPlayerSpace );

	// UNDONE: This algorithm needs a bit more thought.  REVISIT.
	// put the bottom of the object arms' length below eye level
	// get bottommost point of object
	Vector bottom = physcollision->CollideGetExtent( pPhysics->GetCollide(), vec3_origin, angles, Vector(0,0,-1) );

	// get the real eye origin
	Vector playerEye = pPlayer->EyePosition();

	// move target up so that bottom of object is at PLAYER_HOLD_LEVEL z in local space
//	float delta = PLAYER_HOLD_LEVEL_EYES - bottom.z - m_positionPlayerSpace.z;
	float delta = 0;

	// player can reach down 2ft below his feet
	float maxPickup = (playerEye.z + PLAYER_HOLD_LEVEL_EYES) - (pPlayer->GetAbsMins().z - PLAYER_REACH_DOWN_DISTANCE);

	delta = clamp( delta, pPlayer->WorldAlignMins().z, maxPickup );
	m_positionPlayerSpace.z += delta;
	m_anglesPlayerSpace = TransformAnglesToLocalSpace( angles, tmp );

	m_anglesPlayerSpace = AlignAngles( m_anglesPlayerSpace, DOT_30DEGREE );
	
	// re-transform and check
	angles = TransformAnglesToWorldSpace( m_anglesPlayerSpace, tmp );
	VectorTransform( m_positionPlayerSpace, tmp, position );
	// hackhack: Move up to eye position for the check
	float saveZ = position.z;
	position.z = playerEye.z;
	CheckObjectPosition( position, angles, position );
	
	// move back to original position
	position.z = saveZ;

	VectorITransform( position, tmp, m_positionPlayerSpace );
}
void RagdollComputeExactBbox( const ragdoll_t &ragdoll, const Vector &origin, Vector &outMins, Vector &outMaxs )
{
	outMins = origin;
	outMaxs = origin;

	for ( int i = 0; i < ragdoll.listCount; i++ )
	{
		Vector mins, maxs;
		Vector objectOrg;
		QAngle objectAng;
		IPhysicsObject *pObject = ragdoll.list[i].pObject;
		pObject->GetPosition( &objectOrg, &objectAng );
		physcollision->CollideGetAABB( mins, maxs, pObject->GetCollide(), objectOrg, objectAng );
		for ( int j = 0; j < 3; j++ )
		{
			if ( mins[j] < outMins[j] )
			{
				outMins[j] = mins[j];
			}
			if ( maxs[j] > outMaxs[j] )
			{
				outMaxs[j] = maxs[j];
			}
		}
	}
}
//-----------------------------------------------------------------------------
// Purpose: Returns the magnitude of the entity's angular velocity.
//-----------------------------------------------------------------------------
float CPointAngularVelocitySensor::SampleAngularVelocity(CBaseEntity *pEntity)
{
	if (pEntity->GetMoveType() == MOVETYPE_VPHYSICS)
	{
		IPhysicsObject *pPhys = pEntity->VPhysicsGetObject();
		if (pPhys != NULL)
		{
			Vector vecVelocity;
			AngularImpulse vecAngVelocity;
			pPhys->GetVelocity(&vecVelocity, &vecAngVelocity);

			QAngle angles;
			pPhys->GetPosition( NULL, &angles );

			float dt = gpGlobals->curtime - GetLastThink();
			if ( dt == 0 )
				dt = 0.1;

			// HACKHACK: We don't expect a real 'delta' orientation here, just enough of an error estimate to tell if this thing
			// is trying to move, but failing.
			QAngle delta = angles - m_lastOrientation;

			if ( ( delta.Length() / dt )  < ( vecAngVelocity.Length() * 0.01 ) )
			{
				return 0.0f;
			}
			m_lastOrientation = angles;

			if ( m_bUseHelper == false )
			{
				return vecAngVelocity.Length();
			}
			else
			{
				Vector vLine = m_vecAxis - GetAbsOrigin();
				VectorNormalize( vLine );

				Vector vecWorldAngVelocity;
				pPhys->LocalToWorldVector( &vecWorldAngVelocity, vecAngVelocity );
				float flDot = DotProduct( vecWorldAngVelocity, vLine );

				return flDot;
			}
		}
	}
	else
	{
		QAngle vecAngVel = pEntity->GetLocalAngularVelocity();
		float flMax = MAX(fabs(vecAngVel[PITCH]), fabs(vecAngVel[YAW]));

		return MAX(flMax, fabs(vecAngVel[ROLL]));
	}

	return 0;
}
	//Actual work code
	IterationRetval_t EnumElement( IHandleEntity *pHandleEntity )
	{
		C_BaseEntity *pEnt = ClientEntityList().GetBaseEntityFromHandle( pHandleEntity->GetRefEHandle() );
		if ( pEnt == NULL )
			return ITERATION_CONTINUE;

		C_BaseAnimating *pModel = static_cast< C_BaseAnimating * >( pEnt );

		if ( pModel == NULL )
			return ITERATION_CONTINUE;

		trace_t tr;
		enginetrace->ClipRayToEntity( m_rayShot, MASK_SHOT, pModel, &tr );

		IPhysicsObject	*pPhysicsObject = pModel->VPhysicsGetObject();
		
		if ( pPhysicsObject == NULL )
			return ITERATION_CONTINUE;

		if ( tr.fraction < 1.0 )
		{
			IPhysicsObject *pReference = GetWorldPhysObject();

			if ( !pReference || !pPhysicsObject )
				 return ITERATION_CONTINUE;
			
			constraint_ballsocketparams_t ballsocket;
			ballsocket.Defaults();
			
			Vector Origin;
			
			pPhysicsObject->GetPosition( &Origin, NULL );

			if ( ( Origin- m_vWorld).Length () < 64 )
			{
				pReference->WorldToLocal( ballsocket.constraintPosition[0], m_vWorld );
				pPhysicsObject->WorldToLocal( ballsocket.constraintPosition[1], Origin );
				
				GetBreakParams( ballsocket.constraint );

				ballsocket.constraint.torqueLimit = 0;

				m_pConstraint = physenv->CreateBallsocketConstraint( pReference, pPhysicsObject, NULL, ballsocket );

				pPhysicsObject->ApplyForceCenter( Vector(0, 0, 1 ) * 100);
												
				return ITERATION_STOP;
			}
			else
				return ITERATION_CONTINUE;
		}

		return ITERATION_CONTINUE;
	}
bool CStatueProp::TestCollision( const Ray_t &ray, unsigned int fContentsMask, trace_t& tr )
{
	IPhysicsObject *pPhysObject = VPhysicsGetObject();

	if ( pPhysObject )
	{
		Vector vecPosition;
		QAngle vecAngles;
		pPhysObject->GetPosition( &vecPosition, &vecAngles );
		const CPhysCollide *pScaledCollide = pPhysObject->GetCollide();
		physcollision->TraceBox( ray, pScaledCollide, vecPosition, vecAngles, &tr );

		return tr.DidHit();
	}

	return false;
}
//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *pController - 
//			*pObject - 
//			deltaTime - 
//			&linear - 
//			&angular - 
// Output : IMotionEvent::simresult_e
//-----------------------------------------------------------------------------
IMotionEvent::simresult_e CAI_BasePhysicsFlyingBot::Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject, float deltaTime, Vector &linear, AngularImpulse &angular )
{
	static int count;

	IPhysicsObject *pPhysicsObject = VPhysicsGetObject();
	// Assert( pPhysicsObject );
	if (!pPhysicsObject) 
		return SIM_NOTHING;

	// move
	Vector actualVelocity;
	AngularImpulse actualAngularVelocity;
	pPhysicsObject->GetVelocity( &actualVelocity, &actualAngularVelocity );
	linear = (m_vCurrentVelocity - actualVelocity) * (0.1 / deltaTime) * 10.0;

	/*
	DevMsg("Sim %d : %5.1f %5.1f %5.1f\n", count++,
		m_vCurrentVelocity.x - actualVelocity.x, 
		m_vCurrentVelocity.y - actualVelocity.y, 
		m_vCurrentVelocity.z - actualVelocity.z );
	*/

	// do angles.
	Vector actualPosition;
	QAngle actualAngles;
	pPhysicsObject->GetPosition( &actualPosition, &actualAngles ); 

	// FIXME: banking currently disabled, forces simple upright posture
	angular.x = (UTIL_AngleDiff( m_vCurrentBanking.z, actualAngles.z ) - actualAngularVelocity.x) * (1 / deltaTime);
	angular.y = (UTIL_AngleDiff( m_vCurrentBanking.x, actualAngles.x ) - actualAngularVelocity.y) * (1 / deltaTime);

	// turn toward target
	angular.z = UTIL_AngleDiff( m_fHeadYaw, actualAngles.y + actualAngularVelocity.z * 0.1 ) * (1 / deltaTime);

	// angular = m_vCurrentAngularVelocity - actualAngularVelocity;

	// DevMsg("Sim %d : %.1f %.1f %.1f (%.1f)\n", count++, actualAngles.x, actualAngles.y, actualAngles.z, m_fHeadYaw );

	// FIXME: remove the stuff from MoveExecute();
	// FIXME: check MOVE?

	ClampMotorForces( linear, angular );

	return SIM_GLOBAL_ACCELERATION; // on my local axis.   SIM_GLOBAL_ACCELERATION
}
void C_PhysPropClientside::Break()
{
	m_takedamage = DAMAGE_NO;
	
	IPhysicsObject *pPhysics = VPhysicsGetObject();

	Vector velocity;
	AngularImpulse angVelocity;
	Vector origin;
	QAngle angles;
	AddSolidFlags( FSOLID_NOT_SOLID );

	if ( pPhysics )
	{
		pPhysics->GetVelocity( &velocity, &angVelocity );
		pPhysics->GetPosition( &origin, &angles );
		pPhysics->RecheckCollisionFilter();
	}
	else
	{
		velocity = GetAbsVelocity();
		QAngleToAngularImpulse( GetLocalAngularVelocity(), angVelocity );
		origin = GetAbsOrigin();
		angles = GetAbsAngles();
	}

	breakablepropparams_t params( origin, angles, velocity, angVelocity );
	params.impactEnergyScale = m_impactEnergyScale;
	params.defCollisionGroup = GetCollisionGroup();
	if ( params.defCollisionGroup == COLLISION_GROUP_NONE )
	{
		// don't automatically make anything COLLISION_GROUP_NONE or it will
		// collide with debris being ejected by breaking
		params.defCollisionGroup = COLLISION_GROUP_INTERACTIVE;
	}

	// no damage/damage force? set a burst of 100 for some movement
	params.defBurstScale = 100;

	// spwan break chunks
	PropBreakableCreateAll( GetModelIndex(), pPhysics, params, this, -1, false );

	Release(); // destroy object
}
Example #8
0
//-----------------------------------------------------------------------------
// Purpose: Used to tell whether an item may be picked up by the player.  This
//			accounts for solid obstructions being in the way.
// Input  : *pItem - item in question
//			*pPlayer - player attempting the pickup
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool UTIL_ItemCanBeTouchedByPlayer( CBaseEntity *pItem, CBasePlayer *pPlayer )
{
	if ( pItem == NULL || pPlayer == NULL )
		return false;

	// For now, always allow a vehicle riding player to pick up things they're driving over
	if ( pPlayer->IsInAVehicle() )
		return true;

	// Get our test positions
	Vector vecStartPos;
	IPhysicsObject *pPhysObj = pItem->VPhysicsGetObject();
	if ( pPhysObj != NULL )
	{
		// Use the physics hull's center
		QAngle vecAngles;
		pPhysObj->GetPosition( &vecStartPos, &vecAngles );
	}
	else
	{
		// Use the generic bbox center
		vecStartPos = pItem->CollisionProp()->WorldSpaceCenter();
	}

	Vector vecEndPos = pPlayer->EyePosition();

	// FIXME: This is the simple first try solution towards the problem.  We need to take edges and shape more into account
	//		  for this to be fully robust.

	// Trace between to see if we're occluded
	trace_t tr;
	CTraceFilterSkipTwoEntities filter( pPlayer, pItem, COLLISION_GROUP_PLAYER_MOVEMENT );
	UTIL_TraceLine( vecStartPos, vecEndPos, MASK_SOLID, &filter, &tr );

	// Occluded
	// FIXME: For now, we exclude starting in solid because there are cases where this doesn't matter
	if ( tr.fraction < 1.0f )
		return false;

	return true;
}
void PhysicsSceneRenderer::Render(PhysicsScene *p_scene, Camera *p_camera)
{
	vec4 planes[6];
	p_camera->getFrustumPlanes(planes);


	auto & cloths = p_scene->GetCloths();
	for (auto iter = cloths.begin(); iter != cloths.end(); ++iter)
	{
		ICloth *cloth = (*iter);
		IPhysicsObject ** nodes = cloth->GetNodes();

		for (unsigned int r = 0; r < cloth->GetHeight() - 1; ++r)
		{
			for (unsigned int c = 0; c < cloth->GetWidth() - 1; ++c)
			{
				{
					auto obj0 = nodes[r * cloth->GetWidth() + c];
					auto obj1 = nodes[(r + 1) * cloth->GetWidth() + c];
					auto obj2 = nodes[(r + 1) * cloth->GetWidth() + (c + 1)];

					auto col0 = GetRenderInfo(obj0).m_colour;
					auto col1 = GetRenderInfo(obj1).m_colour;
					auto col2 = GetRenderInfo(obj2).m_colour;

					auto col = glm::mix(col0, glm::mix(col1, col2, 0.5F), 0.6667F);

					col.a = 1.0F;

					if (!(obj0->GetHasConstraintBroke() || obj1->GetHasConstraintBroke() || obj2->GetHasConstraintBroke()))
					Gizmos::addTri(obj0->GetPosition(), obj1->GetPosition(), obj2->GetPosition(), col);
				}

				{
					auto obj0 = nodes[r * cloth->GetWidth() + c];
					auto obj1 = nodes[(r + 1) * cloth->GetWidth() + (c + 1)];
					auto obj2 = nodes[r * cloth->GetWidth() + (c + 1)];

					auto col0 = GetRenderInfo(obj0).m_colour;
					auto col1 = GetRenderInfo(obj1).m_colour;
					auto col2 = GetRenderInfo(obj2).m_colour;

					auto col = glm::mix(col0, glm::mix(col1, col2, 0.5F), 0.6667F);

					col.a = 1.0F;

					if (!(obj0->GetHasConstraintBroke() || obj1->GetHasConstraintBroke() || obj2->GetHasConstraintBroke()))
					Gizmos::addTri(obj0->GetPosition(), obj1->GetPosition(), obj2->GetPosition(), col);
				}
			}
		}
	}


	int activeObjects = 0;
	auto & objects = p_scene->GetPhysicsObjects();
	for (auto iter = objects.begin(); iter != objects.end(); ++iter)
	{
		IPhysicsObject *obj = (*iter);

		activeObjects += obj->GetIsAwake();

		RenderInfo &info = GetRenderInfo(obj);

		auto col = info.m_colour;

		if (col.a == 0.0F)
			continue;

		ICollider * collider = obj->GetCollider();

		switch (collider->GetType())
		{
		case ICollider::Type::SPHERE:
		{
			float r = ((SphereCollider*)collider)->GetRadius();

			bool hidden = false;
			for (int i = 0; i < 6; i++) {
				float d = glm::dot(vec3(planes[i]), obj->GetPosition()) +
					planes[i].w;
				if (d < -r)
				{
					hidden = true;
					continue;
				}
			}

			if (hidden)
				continue;

			if (obj->GetIsAwake())
			{
				int j = glm::clamp((int)(r * 4), 2, 4);
				Gizmos::addSphere(obj->GetPosition(), r, j, j * 2, col);
			}

			else
			{
				float r = ((SphereCollider*)collider)->GetRadius();
				Gizmos::addAABBFilled(obj->GetPosition(), vec3(r * 0.875F), col);
			}
		}
			break;

		case ICollider::Type::PLANE:
		{
			vec3 planeNormal = ((PlaneCollider*)collider)->GetNormal();
			float planeDistance = ((PlaneCollider*)collider)->GetDistance();

			glm::mat4 normMatrix = glm::lookAt(vec3(0), planeNormal, vec3(0.000001F, 1, 0));

			vec3 corners[4] = 
			{
				vec3(vec4(-9999, -9999, -planeDistance, 1) * normMatrix),
				vec3(vec4(-9999, +9999, -planeDistance, 1) * normMatrix),
				vec3(vec4(+9999, +9999, -planeDistance, 1) * normMatrix),
				vec3(vec4(+9999, -9999, -planeDistance, 1) * normMatrix),
			};

			Gizmos::addTri(corners[0], corners[1], corners[2], col);
			Gizmos::addTri(corners[0], corners[2], corners[3], col);
		}
			break;

		case ICollider::Type::AABB:
		{
			const vec3 & position = ((AABBCollider*)collider)->GetPosition();
			const vec3 & extends = ((AABBCollider*)collider)->GetHalfExtents();

			Gizmos::addAABBFilled(position, extends, col);
		}
			break;
		}
	}

	auto & constraints = p_scene->GetConstraints();
	for (auto iter = constraints.begin(); iter != constraints.end(); ++iter)
	{
		IConstraint *con = (*iter);

		auto col0 = GetRenderInfo(con->GetObject1()).m_colour;
/*
		if (col0.a == 0.0F)
			continue;*/

		auto col1 = GetRenderInfo(con->GetObject1()).m_colour;
/*
		if (col1.a == 0.0F)
			continue;*/

		auto col = glm::mix(col0, col1, 0.5F);
		col.a = 1.0F;

		Gizmos::addLine(con->GetObject1()->GetPosition(), con->GetObject2()->GetPosition(), col);
	}

	//printf("Physics Objects: %d [AWAKE] / %d [TOTAL]\n", activeObjects, objects.size());
}
void CWeaponGravityGun::EffectUpdate( void )
{
	Vector start, forward, right;
	trace_t tr;

	CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
	if ( !pOwner )
		return;

	pOwner->EyeVectors( &forward, &right, NULL );

	start = pOwner->Weapon_ShootPosition();

	TraceLine( &tr );
	Vector end = tr.endpos;
	float distance = tr.fraction * 4096;

	if ( m_hObject == NULL && tr.DidHitNonWorldEntity() )
	{
		CBaseEntity *pEntity = tr.m_pEnt;
		AttachObject( pEntity, GetPhysObjFromPhysicsBone( pEntity, tr.physicsbone ), tr.physicsbone, start, tr.endpos, distance );
	}

	// Add the incremental player yaw to the target transform
	QAngle angles = m_gravCallback.TransformAnglesFromPlayerSpace( m_gravCallback.m_targetRotation, pOwner );

	CBaseEntity *pObject = m_hObject;
	if ( pObject )
	{
		if ( m_useDown )
		{
			if ( pOwner->m_afButtonPressed & IN_USE )
			{
				m_useDown = false;
			}
		}
		else 
		{
			if ( pOwner->m_afButtonPressed & IN_USE )
			{
				m_useDown = true;
			}
		}

		if ( m_useDown )
		{
#ifndef CLIENT_DLL
			pOwner->SetPhysicsFlag( PFLAG_DIROVERRIDE, true );
#endif
			if ( pOwner->m_nButtons & IN_FORWARD )
			{
				m_distance = Approach( 1024, m_distance, gpGlobals->frametime * 100 );
			}
			if ( pOwner->m_nButtons & IN_BACK )
			{
				m_distance = Approach( 40, m_distance, gpGlobals->frametime * 100 );
			}
		}

		if ( pOwner->m_nButtons & IN_WEAPON1 )
		{
			m_distance = Approach( 1024, m_distance, m_distance * 0.1 );
		}
		if ( pOwner->m_nButtons & IN_WEAPON2 )
		{
			m_distance = Approach( 40, m_distance, m_distance * 0.1 );
		}

		IPhysicsObject *pPhys = GetPhysObjFromPhysicsBone( pObject, m_physicsBone );
		if ( pPhys )
		{
			if ( pPhys->IsAsleep() )
			{
				// on the odd chance that it's gone to sleep while under anti-gravity
				pPhys->Wake();
			}

			Vector newPosition = start + forward * m_distance;
			Vector offset;
			pPhys->LocalToWorld( &offset, m_worldPosition );
			Vector vecOrigin;
			pPhys->GetPosition( &vecOrigin, NULL );
			m_gravCallback.SetTargetPosition( newPosition + (vecOrigin - offset), angles );
			Vector dir = (newPosition - pObject->GetLocalOrigin());
			m_movementLength = dir.Length();
		}
	}
	else
	{
		m_targetPosition = end;
		//m_gravCallback.SetTargetPosition( end, m_gravCallback.m_targetRotation );
	}
}
Example #11
0
void CHLPlayerMove::FinishMove( CBasePlayer *player, CUserCmd *ucmd, CMoveData *move )
{
	// Call the default FinishMove code.
	BaseClass::FinishMove( player, ucmd, move );
	if ( gpGlobals->frametime != 0 )
	{		
		float distance = 0.0f;
		IServerVehicle *pVehicle = player->GetVehicle();
		if ( pVehicle )
		{
			pVehicle->FinishMove( player, ucmd, move );
			IPhysicsObject *obj = player->GetVehicleEntity()->VPhysicsGetObject();
			if ( obj )
			{
				Vector newPos;
				obj->GetPosition( &newPos, NULL );
				distance = VectorLength( newPos - m_vecSaveOrigin );
				if ( m_vecSaveOrigin == vec3_origin || distance > 100.0f )
					distance = 0.0f;
				m_vecSaveOrigin = newPos;
			}
			
			CPropVehicleDriveable *driveable = dynamic_cast< CPropVehicleDriveable * >( player->GetVehicleEntity() );
			if ( driveable )
			{
				// Overturned and at rest (if still moving it can fix itself)
				bool bFlipped = driveable->IsOverturned() && ( distance < 0.5f );
				if ( m_bVehicleFlipped != bFlipped )
				{
					if ( bFlipped )
					{
						gamestats->Event_FlippedVehicle( player, driveable );
					}
					m_bVehicleFlipped = bFlipped;
				}
			}
			else
			{
				m_bVehicleFlipped = false;
			}
		}
		else
		{
			m_bVehicleFlipped = false;
			distance = VectorLength( player->GetAbsOrigin() - m_vecSaveOrigin );
		}
		if ( distance > 0 )
		{
			gamestats->Event_PlayerTraveled( player, distance, pVehicle ? true : false, !pVehicle && static_cast< CHL2_Player * >( player )->IsSprinting() );
		}
	}

	bool bGodMode = ( player->GetFlags() & FL_GODMODE ) ? true : false;
	if ( m_bInGodMode != bGodMode )
	{
		m_bInGodMode = bGodMode;
		if ( bGodMode )
		{
			gamestats->Event_PlayerEnteredGodMode( player );
		}
	}
	bool bNoClip = ( player->GetMoveType() == MOVETYPE_NOCLIP );
	if ( m_bInNoClip != bNoClip )
	{
		m_bInNoClip = bNoClip;
		if ( bNoClip )
		{
			gamestats->Event_PlayerEnteredNoClip( player );
		}
	}
}
Example #12
0
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CRagdollProp::HandleFirstCollisionInteractions( int index, gamevcollisionevent_t *pEvent )
{
	IPhysicsObject *pObj = VPhysicsGetObject();
	if ( !pObj)
		return;

	if( HasPhysgunInteraction( "onfirstimpact", "break" ) )
	{
		// Looks like it's best to break by having the object damage itself. 
		CTakeDamageInfo info;

		info.SetDamage( m_iHealth );
		info.SetAttacker( this );
		info.SetInflictor( this );
		info.SetDamageType( DMG_GENERIC );

		Vector vecPosition;
		Vector vecVelocity;

		VPhysicsGetObject()->GetVelocity( &vecVelocity, NULL );
		VPhysicsGetObject()->GetPosition( &vecPosition, NULL );

		info.SetDamageForce( vecVelocity );
		info.SetDamagePosition( vecPosition );

		TakeDamage( info );
		return;
	}

	if( HasPhysgunInteraction( "onfirstimpact", "paintsplat" ) )
	{
		IPhysicsObject *pObj = VPhysicsGetObject();
 
		Vector vecPos;
		pObj->GetPosition( &vecPos, NULL );
 
		trace_t tr;
		UTIL_TraceLine( vecPos, vecPos + pEvent->preVelocity[0] * 1.5, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );

		switch( random->RandomInt( 1, 3 ) )
		{
		case 1:
			UTIL_DecalTrace( &tr, "PaintSplatBlue" );
			break;

		case 2:
			UTIL_DecalTrace( &tr, "PaintSplatGreen" );
			break;

		case 3:
			UTIL_DecalTrace( &tr, "PaintSplatPink" );
			break;
		}
	}

	bool bAlienBloodSplat = HasPhysgunInteraction( "onfirstimpact", "alienbloodsplat" );
	if( bAlienBloodSplat || HasPhysgunInteraction( "onfirstimpact", "bloodsplat" ) )
	{
		IPhysicsObject *pObj = VPhysicsGetObject();
 
		Vector vecPos;
		pObj->GetPosition( &vecPos, NULL );
 
		trace_t tr;
		UTIL_TraceLine( vecPos, vecPos + pEvent->preVelocity[0] * 1.5, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );

		UTIL_BloodDecalTrace( &tr, bAlienBloodSplat ? BLOOD_COLOR_GREEN : BLOOD_COLOR_RED );
	}
}
//-----------------------------------------------------------------------------
// Debugging methods
//-----------------------------------------------------------------------------
void CFourWheelVehiclePhysics::DrawDebugGeometryOverlays()
{
	Vector vecRad(m_debugRadius,m_debugRadius,m_debugRadius);
	for ( int i = 0; i < m_wheelCount; i++ )
	{
		NDebugOverlay::BoxAngles(m_wheelPosition[i], -vecRad, vecRad, m_wheelRotation[i], 0, 255, 45, 0 ,0);
	}

	for ( int iWheel = 0; iWheel < m_wheelCount; iWheel++ )
	{
		IPhysicsObject *pWheel = m_pVehicle->GetWheel( iWheel );
		
		Vector vecPos;
		QAngle vecRot;
		pWheel->GetPosition( &vecPos, &vecRot );

		NDebugOverlay::BoxAngles( vecPos, -vecRad, vecRad, vecRot, 0, 255, 45, 0 ,0 );
	}

#if 1
	// Render vehicle data.
	IPhysicsObject *pBody = m_pOuter->VPhysicsGetObject();
	if ( pBody )
	{
		const vehicleparams_t vehicleParams = m_pVehicle->GetVehicleParams();

		// Draw a red cube as the "center" of the vehicle.
		Vector vecBodyPosition; 
		QAngle angBodyDirection;
		pBody->GetPosition( &vecBodyPosition, &angBodyDirection );
		NDebugOverlay::BoxAngles( vecBodyPosition, Vector( -5, -5, -5 ), Vector( 5, 5, 5 ), angBodyDirection, 255, 0, 0, 0 ,0 );

		matrix3x4_t matrix;
		AngleMatrix( angBodyDirection, vecBodyPosition, matrix );

		// Draw green cubes at axle centers.
		Vector vecAxlePositions[2], vecAxlePositionsHL[2];
		vecAxlePositions[0] = vehicleParams.axles[0].offset;
		vecAxlePositions[1] = vehicleParams.axles[1].offset;

		VectorTransform( vecAxlePositions[0], matrix, vecAxlePositionsHL[0] );		
		VectorTransform( vecAxlePositions[1], matrix, vecAxlePositionsHL[1] );

		NDebugOverlay::BoxAngles( vecAxlePositionsHL[0], Vector( -3, -3, -3 ), Vector( 3, 3, 3 ), angBodyDirection, 0, 255, 0, 0 ,0 );
		NDebugOverlay::BoxAngles( vecAxlePositionsHL[1], Vector( -3, -3, -3 ), Vector( 3, 3, 3 ), angBodyDirection, 0, 255, 0, 0 ,0 );

		// Draw blue cubes at wheel centers.
		Vector vecWheelPositions[4], vecWheelPositionsHL[4];
		vecWheelPositions[0] = vehicleParams.axles[0].offset;
		vecWheelPositions[0] += vehicleParams.axles[0].wheelOffset;
		vecWheelPositions[1] = vehicleParams.axles[0].offset;
		vecWheelPositions[1] -= vehicleParams.axles[0].wheelOffset;
		vecWheelPositions[2] = vehicleParams.axles[1].offset;
		vecWheelPositions[2] += vehicleParams.axles[1].wheelOffset;
		vecWheelPositions[3] = vehicleParams.axles[1].offset;
		vecWheelPositions[3] -= vehicleParams.axles[1].wheelOffset;

		VectorTransform( vecWheelPositions[0], matrix, vecWheelPositionsHL[0] );
		VectorTransform( vecWheelPositions[1], matrix, vecWheelPositionsHL[1] );
		VectorTransform( vecWheelPositions[2], matrix, vecWheelPositionsHL[2] );
		VectorTransform( vecWheelPositions[3], matrix, vecWheelPositionsHL[3] );

		float flWheelRadius = vehicleParams.axles[0].wheels.radius;
		flWheelRadius = IVP2HL( flWheelRadius );
		Vector vecWheelRadius( flWheelRadius, flWheelRadius, flWheelRadius );

		NDebugOverlay::BoxAngles( vecWheelPositionsHL[0], -vecWheelRadius, vecWheelRadius, angBodyDirection, 0, 0, 255, 0 ,0 );
		NDebugOverlay::BoxAngles( vecWheelPositionsHL[1], -vecWheelRadius, vecWheelRadius, angBodyDirection, 0, 0, 255, 0 ,0 );
		NDebugOverlay::BoxAngles( vecWheelPositionsHL[2], -vecWheelRadius, vecWheelRadius, angBodyDirection, 0, 0, 255, 0 ,0 );
		NDebugOverlay::BoxAngles( vecWheelPositionsHL[3], -vecWheelRadius, vecWheelRadius, angBodyDirection, 0, 0, 255, 0 ,0 );

		// Draw wheel raycasts in yellow
		vehicle_debugcarsystem_t debugCarSystem;
		m_pVehicle->GetCarSystemDebugData( debugCarSystem );
		for ( int iWheel = 0; iWheel < 4; ++iWheel )
		{
			Vector vecStart, vecEnd, vecImpact;

			// Hack for now.
			float tmpY = IVP2HL( debugCarSystem.vecWheelRaycasts[iWheel][0].z );
			vecStart.z = -IVP2HL( debugCarSystem.vecWheelRaycasts[iWheel][0].y );
			vecStart.y = tmpY;
			vecStart.x = IVP2HL( debugCarSystem.vecWheelRaycasts[iWheel][0].x );

			tmpY = IVP2HL( debugCarSystem.vecWheelRaycasts[iWheel][1].z );
			vecEnd.z = -IVP2HL( debugCarSystem.vecWheelRaycasts[iWheel][1].y );
			vecEnd.y = tmpY;
			vecEnd.x = IVP2HL( debugCarSystem.vecWheelRaycasts[iWheel][1].x );

			tmpY = IVP2HL( debugCarSystem.vecWheelRaycastImpacts[iWheel].z );
			vecImpact.z = -IVP2HL( debugCarSystem.vecWheelRaycastImpacts[iWheel].y );
			vecImpact.y = tmpY;
			vecImpact.x = IVP2HL( debugCarSystem.vecWheelRaycastImpacts[iWheel].x );

			NDebugOverlay::BoxAngles( vecStart, Vector( -1 , -1, -1 ), Vector( 1, 1, 1 ), angBodyDirection, 0, 255, 0, 0, 0  );
			NDebugOverlay::Line( vecStart, vecEnd, 255, 255, 0, true, 0 );
			NDebugOverlay::BoxAngles( vecEnd, Vector( -1, -1, -1 ), Vector( 1, 1, 1 ), angBodyDirection, 255, 0, 0, 0, 0 );

			NDebugOverlay::BoxAngles( vecImpact, Vector( -0.5f , -0.5f, -0.5f ), Vector( 0.5f, 0.5f, 0.5f ), angBodyDirection, 0, 0, 255, 0, 0  );
		}
	}
#endif
}