Vector Pickup_PhysGunLaunchVelocity( CBaseEntity *pObject, const Vector &vecForward, PhysGunForce_t reason )
{
	// The object must be valid
	if ( pObject == NULL )
	{
		Assert( 0 );
		return vec3_origin;
	}

	// Shouldn't ever get here with a non-vphysics object.
	IPhysicsObject *pPhysicsObject = pObject->VPhysicsGetObject();
	if ( pPhysicsObject == NULL )
	{
		Assert( 0 );
		return vec3_origin;
	}

	// Call the pickup entity's callback
	IPlayerPickupVPhysics *pPickup = dynamic_cast<IPlayerPickupVPhysics *>(pObject);
	if ( pPickup != NULL && pPickup->ShouldPuntUseLaunchForces( reason ) )
		return pPickup->PhysGunLaunchVelocity( vecForward, pPhysicsObject->GetMass() );

	// Do our default behavior
	return Pickup_DefaultPhysGunLaunchVelocity(	vecForward, pPhysicsObject->GetMass() );
}
Example #2
0
//---------------------------------------------------------
// A different bounce behavior for the citizen-modified mine. Detonates at the top of its apex, 
// and does not attempt to track enemies.
//---------------------------------------------------------
void CBounceBomb::CavernBounceThink()
{
	SetNextThink( gpGlobals->curtime + 0.1 );
	StudioFrameAdvance();

	IPhysicsObject *pPhysicsObject = VPhysicsGetObject();

	if ( pPhysicsObject != NULL )
	{
		const float MINE_MAX_JUMP_HEIGHT = 78;

		// Figure out how much headroom the mine has, and hop to within a few inches of that.
		trace_t tr;
		UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + Vector( 0, 0, MINE_MAX_JUMP_HEIGHT ), MASK_SHOT, this, COLLISION_GROUP_INTERACTIVE, &tr );

		float height;

		if( tr.m_pEnt && tr.m_pEnt->VPhysicsGetObject() )
		{
			// Physics object resting on me. Jump as hard as allowed to try to knock it away.
			height = MINE_MAX_JUMP_HEIGHT;
		}
		else
		{
			height = tr.endpos.z - GetAbsOrigin().z;
			height -= BOUNCEBOMB_RADIUS;
			if ( height < 0.1 )
				height = 0.1;
		}

		float time = sqrt( height / (0.5 * sv_gravity.GetFloat()) );
		float velocity = sv_gravity.GetFloat() * time;

		// or you can just AddVelocity to the object instead of ApplyForce
		float force = velocity * pPhysicsObject->GetMass();

		Vector up;

		GetVectors( NULL, NULL, &up );
		
		pPhysicsObject->Wake();
		pPhysicsObject->ApplyForceCenter( up * force );
		if( m_hNearestNPC )
		{
			Vector vecPredict = m_hNearestNPC->GetSmoothedVelocity();

			pPhysicsObject->ApplyForceCenter( vecPredict * (pPhysicsObject->GetMass() * 0.65f) );
		}

		pPhysicsObject->ApplyTorqueCenter( AngularImpulse( random->RandomFloat( 15, 40 ), random->RandomFloat( 15, 40 ), random->RandomFloat( 30, 60 ) ) );
		
		EmitSound( "NPC_CombineMine.Hop" );

		SetThink( &CBounceBomb::ExplodeThink );
		SetNextThink( gpGlobals->curtime + 0.33f );
	}
}
Example #3
0
//-----------------------------------------------------------------------------
// Purpose: Adds the entity's mass to the aggregate mass consumed
//-----------------------------------------------------------------------------
void CGravityVortexController::ConsumeEntity( CBaseEntity *pEnt )
{
	// Get our base physics object
	IPhysicsObject *pPhysObject = pEnt->VPhysicsGetObject();
	if ( pPhysObject == NULL )
		return;

	// Ragdolls need to report the sum of all their parts
	CRagdollProp *pRagdoll = dynamic_cast< CRagdollProp* >( pEnt );
	if ( pRagdoll != NULL )
	{		
		// Find the aggregate mass of the whole ragdoll
		ragdoll_t *pRagdollPhys = pRagdoll->GetRagdoll();
		for ( int j = 0; j < pRagdollPhys->listCount; ++j )
		{
			m_flMass += pRagdollPhys->list[j].pObject->GetMass();
		}
	}
	else
	{
		// Otherwise we just take the normal mass
		m_flMass += pPhysObject->GetMass();
	}

	// Destroy the entity
	UTIL_Remove( pEnt );
}
//-----------------------------------------------------------------------------
// Purpose:
// Input  :
// Output :
//-----------------------------------------------------------------------------
int CTDP_NPC_CombineS::TakeDamage( const CTakeDamageInfo &info )
{
	if( info.GetInflictor() && info.GetInflictor()->VPhysicsGetObject() )
	{
		// Hit by a physics object! Was I blocking?
		if( m_fIsBlocking )
		{
			IPhysicsObject *pPhysObject;

			pPhysObject = info.GetInflictor()->VPhysicsGetObject();

			if( pPhysObject )
			{
				// Only deflect objects of relatively low mass
				//DevMsg( "MASS: %f\n", pPhysObject->GetMass() );

				if( pPhysObject->GetMass() <= 30.0 )
				{
					// No damage from light objects (tuned for melons)
					return 0;
				}
			}
		}
	}

	BaseClass::TakeDamage( info );
	return 0;
}
void CPlayerPickupController::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
	if ( ToBasePlayer(pActivator) == m_pPlayer )
	{
		CBaseEntity *pAttached = m_grabController.GetAttached();

		// UNDONE: Use vphysics stress to decide to drop objects
		// UNDONE: Must fix case of forcing objects into the ground you're standing on (causes stress) before that will work
		if ( !pAttached || useType == USE_OFF || (m_pPlayer->m_nButtons & IN_ATTACK2) || m_grabController.ComputeError() > 12 )
		{
			Shutdown();
			return;
		}
		
		//Adrian: Oops, our object became motion disabled, let go!
		IPhysicsObject *pPhys = pAttached->VPhysicsGetObject();
		if ( pPhys && pPhys->IsMoveable() == false )
		{
			Shutdown();
			return;
		}

#if STRESS_TEST
		vphysics_objectstress_t stress;
		CalculateObjectStress( pPhys, pAttached, &stress );
		if ( stress.exertedStress > 250 )
		{
			Shutdown();
			return;
		}
#endif

#ifndef PLAYER_DISABLE_THROWING
		// +ATTACK will throw phys objects
		if ( m_pPlayer->m_nButtons & IN_ATTACK )
		{
			Shutdown( true );
			Vector vecLaunch;
			m_pPlayer->EyeVectors( &vecLaunch );
			// JAY: Scale this with mass because some small objects really go flying
			float massFactor = clamp( pPhys->GetMass(), 0.5, 15 );
			massFactor = RemapVal( massFactor, 0.5, 15, 0.5, 4 );
			vecLaunch *= player_throwforce.GetFloat() * massFactor;

			pPhys->ApplyForceCenter( vecLaunch );
			AngularImpulse aVel = RandomAngularImpulse( -10, 10 ) * massFactor;
			pPhys->ApplyTorqueCenter( aVel );
			return;
		}
#endif
		if ( useType == USE_SET )
		{
			// update position
			m_grabController.UpdateObject( m_pPlayer, 12 );
		}
	}
}
Example #6
0
//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CPhysMagnet::DoMagnetSuck( CBaseEntity *pOther )
{
	if ( !HasSpawnFlags( SF_MAGNET_SUCK ) )
		return;

	if ( !m_bActive )
		return;

	// Don't repeatedly suck
	if ( m_flNextSuckTime > gpGlobals->curtime )
		return;
	
	// Look for physics objects underneath the magnet and suck them onto it
	Vector vecCheckPos, vecSuckPoint;
	VectorTransform( Vector(0,0,-96), EntityToWorldTransform(), vecCheckPos );
	VectorTransform( Vector(0,0,-64), EntityToWorldTransform(), vecSuckPoint );

	CBaseEntity *pEntities[20];
	int iNumEntities = UTIL_EntitiesInSphere( pEntities, 20, vecCheckPos, 80.0, 0 );
	for ( int i = 0; i < iNumEntities; i++ )
	{
		CBaseEntity *pEntity = pEntities[i];
		if ( !pEntity || pEntity == pOther )
			continue;

		IPhysicsObject *pPhys = pEntity->VPhysicsGetObject();
		if ( pPhys && pEntity->GetMoveType() == MOVETYPE_VPHYSICS && pPhys->GetMass() < 5000 )
		{
			// Do we have line of sight to it?
			trace_t tr;
			UTIL_TraceLine( GetAbsOrigin(), pEntity->GetAbsOrigin(), MASK_SHOT, this, 0, &tr );
			if ( tr.fraction == 1.0 || tr.m_pEnt == pEntity )
			{
				// Pull it towards the magnet
				Vector vecVelocity = (vecSuckPoint - pEntity->GetAbsOrigin());
				VectorNormalize(vecVelocity);
				vecVelocity *= 5 * pPhys->GetMass();
				pPhys->AddVelocity( &vecVelocity, NULL );
			}
		}
	}

	m_flNextSuckTime = gpGlobals->curtime + 2.0;
}
void CWeaponGravityGun::SoundUpdate( void )
{
	int newState;
	
	if ( m_hObject )
		newState = SS_LOCKEDON;
	else
		newState = SS_SCANNING;

	if ( newState != m_soundState )
	{
		SoundStop();
		m_soundState = newState;
		SoundStart();
	}

	switch( m_soundState )
	{
	case SS_SCANNING:
		break;
	case SS_LOCKEDON:
		{
			CPASAttenuationFilter filter( this );

			float height = m_hObject->GetAbsOrigin().z - m_originalObjectPosition.z;

			// go from pitch 90 to 150 over a height of 500
			int pitch = 90 + (int)UTIL_LineFraction( height, 0, 500, 60 );

			assert(m_sndLockedOn!=NULL);
			if ( m_sndLockedOn != NULL )
			{
				(CSoundEnvelopeController::GetController()).SoundChangePitch( m_sndLockedOn, pitch, 0.0f );
			}

			// attenutate the movement sounds over 200 units of movement
			float distance = UTIL_LineFraction( m_movementLength, 0, 200, 1.0 );

			// blend the "mass" sounds between 50 and 500 kg
			IPhysicsObject *pPhys = GetPhysObjFromPhysicsBone( m_hObject, m_physicsBone );
			if ( pPhys == NULL )
			{
				// we no longer exist!
				break;
			}
			
			float fade = UTIL_LineFraction( pPhys->GetMass(), 50, 500, 1.0 );

			(CSoundEnvelopeController::GetController()).SoundChangeVolume( m_sndLightObject, fade * distance, 0.0f );

			(CSoundEnvelopeController::GetController()).SoundChangeVolume( m_sndHeavyObject, (1.0 - fade) * distance, 0.0f );
		}
		break;
	}
}
static bool InContactWithHeavyObject( IPhysicsObject *pObject, float heavyMass )
{
	bool contact = false;
	IPhysicsFrictionSnapshot *pSnapshot = pObject->CreateFrictionSnapshot();
	while ( pSnapshot->IsValid() )
	{
		IPhysicsObject *pOther = pSnapshot->GetObject( 1 );
		if ( !pOther->IsMoveable() || pOther->GetMass() > heavyMass )
		{
			contact = true;
			break;
		}
		pSnapshot->NextFrictionData();
	}
	pObject->DestroyFrictionSnapshot( pSnapshot );
	return contact;
}
Example #9
0
bool CZombie::IsSquashed( const CTakeDamageInfo &info )
{
	if( GetHealth() > 0 )
		return false;

	if( info.GetDamageType() & DMG_CRUSH )
	{
		IPhysicsObject *pCrusher = info.GetInflictor()->VPhysicsGetObject();
		if( pCrusher && pCrusher->GetMass() >= ZOMBIE_SQUASH_MASS && info.GetInflictor()->WorldSpaceCenter().z > EyePosition().z )
			// This heuristic detects when a zombie has been squashed from above by a heavy
			// item. Done specifically so we can add gore effects to Ravenholm cartraps.
			// The zombie must take physics damage from a 300+kg object that is centered above its eyes (comes from above)
			return true;
	}

	return false;
}
CBaseEntity *CreateServerRagdoll( CBaseAnimating *pAnimating, int forceBone, const CTakeDamageInfo &info, int collisionGroup )
{
	SyncAnimatingWithPhysics( pAnimating );

	CRagdollProp *pRagdoll = (CRagdollProp *)CBaseEntity::CreateNoSpawn( "prop_ragdoll", pAnimating->GetAbsOrigin(), vec3_angle, NULL );
	pRagdoll->CopyAnimationDataFrom( pAnimating );

	pRagdoll->InitRagdollAnimation();
	matrix3x4_t pBoneToWorld[MAXSTUDIOBONES];
	pAnimating->SetupBones( pBoneToWorld, BONE_USED_BY_ANYTHING );
	
	// Is this a vehicle / NPC collision?
	if ( (info.GetDamageType() & DMG_VEHICLE) && pAnimating->MyNPCPointer() )
	{
		// init the ragdoll with no forces
		pRagdoll->InitRagdoll( vec3_origin, -1, vec3_origin, pBoneToWorld, pBoneToWorld, 0.1, collisionGroup, true );

		// apply vehicle forces
		// Get a list of bones with hitboxes below the plane of impact
		int boxList[128];
		Vector normal(0,0,-1);
		int count = pAnimating->GetHitboxesFrontside( boxList, ARRAYSIZE(boxList), normal, DotProduct( normal, info.GetDamagePosition() ) );
		
		// distribute force over mass of entire character
		float massScale = Studio_GetMass(pAnimating->GetModelPtr());
		massScale = clamp( massScale, 1, 1e4 );
		massScale = 1 / massScale;

		// distribute the force
		// BUGBUG: This will hit the same bone twice if it has two hitboxes!!!!
		ragdoll_t *pRagInfo = pRagdoll->GetRagdoll();
		for ( int i = 0; i < count; i++ )
		{
			int physBone = pAnimating->GetPhysicsBone( pAnimating->GetHitboxBone( boxList[i] ) );
			IPhysicsObject *pPhysics = pRagInfo->list[physBone].pObject;
			pPhysics->ApplyForceCenter( info.GetDamageForce() * pPhysics->GetMass() * massScale );
		}
	}
	else
	{
		pRagdoll->InitRagdoll( info.GetDamageForce(), forceBone, info.GetDamagePosition(), pBoneToWorld, pBoneToWorld, 0.1, collisionGroup, true );
	}

	return pRagdoll;
}
//-----------------------------------------------------------------------------
// Purpose: Push a physics object in our wash. Return false if it's now out of our wash
//-----------------------------------------------------------------------------
bool CBaseHelicopter::DoWashPush( washentity_t *pWash, Vector vecWashOrigin )
{
	if ( !pWash || !pWash->hEntity.Get() )
		return false;

	// Make sure the entity is still within our wash's radius
	CBaseEntity *pEntity = pWash->hEntity;
	Vector vecSpot = pEntity->BodyTarget( vecWashOrigin );
	Vector vecToSpot = ( vecSpot - vecWashOrigin );
	vecToSpot.z = 0;
	float flDist = VectorNormalize( vecToSpot );
	if ( flDist > BASECHOPPER_WASH_RADIUS )
		return false;

	IPhysicsObject *pPhysObject = pEntity->VPhysicsGetObject();

	if ( pPhysObject == NULL )
		return false;

	// Push it away from the center of the wash
	float flMass = pPhysObject->GetMass();
	float flPushTime = (gpGlobals->curtime - pWash->flWashStartTime);
	float flMinPush = BASECHOPPER_WASH_PUSH_MIN * flMass;
	float flMaxPush = BASECHOPPER_WASH_PUSH_MAX * flMass;
	float flWashAmount = min( flMaxPush, RemapVal( flPushTime, 0, BASECHOPPER_WASH_RAMP_TIME, flMinPush, flMaxPush ) );
	Vector vecForce = flWashAmount * vecToSpot * phys_pushscale.GetFloat();
	pEntity->VPhysicsTakeDamage( CTakeDamageInfo( this, this, vecForce, vecWashOrigin, flWashAmount, DMG_BLAST ) );

	// Debug
	if ( g_debug_basehelicopter.GetInt() == BASECHOPPER_DEBUG_WASH )
	{
		NDebugOverlay::Cross3D( pEntity->GetAbsOrigin(), -Vector(4,4,4), Vector(4,4,4), 255, 0, 0, true, 0.1f );
		NDebugOverlay::Line( pEntity->GetAbsOrigin(), pEntity->GetAbsOrigin() + vecForce, 255, 255, 0, true, 0.1f );

		IPhysicsObject *pPhysObject = pEntity->VPhysicsGetObject();
		Msg("Pushed %s (mass %f) with force %f (min %.2f max %.2f) at time %.2f\n", pEntity->GetClassname(), pPhysObject->GetMass(), flWashAmount, flMinPush, flMaxPush, gpGlobals->curtime );
	}

	// If we've pushed this thing for some time, remove it to give us a chance to find lighter things nearby
	if ( flPushTime > 2.0 )
		return false;

	return true;
}
Example #12
0
//-----------------------------------------------------------------------------
// Purpose: One of our magnet constraints broke
//-----------------------------------------------------------------------------
void CPhysMagnet::ConstraintBroken( IPhysicsConstraint *pConstraint )
{
	// Find the entity that was constrained and release it
	int iCount = m_MagnettedEntities.Count();
	for ( int i = 0; i < iCount; i++ )
	{
		if ( m_MagnettedEntities[i].pConstraint == pConstraint )
		{
			IPhysicsObject *pPhysObject = m_MagnettedEntities[i].hEntity->VPhysicsGetObject();
			m_flTotalMass -= pPhysObject->GetMass();

			m_MagnettedEntities.Remove(i);
			break;
		}
	}

	m_OnMagnetDetach.FireOutput( this, this );

	physenv->DestroyConstraint( pConstraint  );
}
Example #13
0
//-----------------------------------------------------------------------------
// Purpose: Get the force that we should add to this NPC's ragdoll.
// Input  : *pNPC - 
// Output : Vector
//
// NOTE: This function assumes pNPC is within this magnet's radius.
//-----------------------------------------------------------------------------
Vector CRagdollMagnet::GetForceVector( CBaseEntity *pNPC )
{
	Vector vecForceToApply;

	if( IsBarMagnet() )
	{
		CPlane axis;
		Vector vecForceDir;
		Vector vecClosest;

		CalcClosestPointOnLineSegment( pNPC->WorldSpaceCenter(), GetAbsOrigin(), m_axis, vecClosest, NULL );

		vecForceDir = (vecClosest - pNPC->WorldSpaceCenter() );
		VectorNormalize( vecForceDir );

		vecForceToApply = vecForceDir * m_force;
	}
	else
	{
		Vector vecForce;

		vecForce = GetAbsOrigin() - pNPC->WorldSpaceCenter();
		VectorNormalize( vecForce );

		vecForceToApply = vecForce * m_force;
	}

	if( ai_debug_ragdoll_magnets.GetBool() )
	{
		IPhysicsObject *pPhysObject;

		pPhysObject = pNPC->VPhysicsGetObject();

		if( pPhysObject )
		{
			Msg("Ragdoll magnet adding %f inches/sec to %s\n", m_force/pPhysObject->GetMass(), pNPC->GetClassname() );
		}
	}

	return vecForceToApply;
}
Example #14
0
void CWeaponGravityGun::AttachObject( CBaseEntity *pObject, const Vector& start, const Vector &end, float distance )
{
	m_hObject = pObject;
	m_useDown = false;
	IPhysicsObject *pPhysics = pObject ? (pObject->VPhysicsGetObject()) : NULL;
	if ( pPhysics && pObject->GetMoveType() == MOVETYPE_VPHYSICS )
	{
		m_distance = distance;

		m_gravCallback.AttachEntity( pObject, pPhysics, end );
		float mass = pPhysics->GetMass();
		Msg( "Object mass: %.2f lbs (%.2f kg)\n", kg2lbs(mass), mass );
		float vel = phys_gunvel.GetFloat();
		if ( mass > phys_gunmass.GetFloat() )
		{
			vel = (vel*phys_gunmass.GetFloat())/mass;
		}
		m_gravCallback.SetMaxVelocity( vel );
//		Msg( "Object mass: %.2f lbs (%.2f kg) %f %f %f\n", kg2lbs(mass), mass, pObject->GetAbsOrigin().x, pObject->GetAbsOrigin().y, pObject->GetAbsOrigin().z );
//		Msg( "ANG: %f %f %f\n", pObject->GetAbsAngles().x, pObject->GetAbsAngles().y, pObject->GetAbsAngles().z );

		m_originalObjectPosition = pObject->GetAbsOrigin();

		m_pelletAttract = -1;
		m_pelletHeld = -1;

		pPhysics->Wake();
		SortPelletsForObject( pObject );
		
		CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
		if( pOwner )
		{
			Pickup_OnPhysGunPickup( pObject, pOwner );
		}
	}
	else
	{
		m_hObject = NULL;
	}
}
Example #15
0
//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *pActivator - 
//			*pCaller - 
//			useType - 
//			value - 
//-----------------------------------------------------------------------------
void CPhysImpact::InputImpact( inputdata_t &inputdata )
{
	Vector	dir;
	trace_t	trace;

	AngleVectors( GetAbsAngles(), &dir );
	
	//Setup our trace information
	float	dist	= HasSpawnFlags( bitsPHYSIMPACT_INFINITE_LENGTH ) ? MAX_TRACE_LENGTH : m_distance;
	Vector	start	= GetAbsOrigin();
	Vector	end		= start + ( dir * dist );

	//Trace out
	UTIL_TraceLine( start, end, MASK_SHOT, this, 0, &trace );

	if ( trace.fraction != 1.0 )
	{
		CBaseEntity	*pEnt = trace.m_pEnt;
	
		IPhysicsObject *pPhysics = pEnt->VPhysicsGetObject();
		//If the entity is valid, hit it
		if ( ( pEnt != NULL  ) && ( pPhysics != NULL ) )
		{
			//Damage falls off unless specified or the ray's length is infinite
			float	damage = HasSpawnFlags( bitsPHYSIMPACT_NOFALLOFF | bitsPHYSIMPACT_INFINITE_LENGTH ) ? 
								m_damage : (m_damage * (1.0f-trace.fraction));
			
			if ( HasSpawnFlags( bitsPHYSIMPACT_IGNORE_MASS ) )
			{
				damage *= pPhysics->GetMass();
			}

			pPhysics->ApplyForceOffset( -damage * trace.plane.normal * phys_pushscale.GetFloat(), trace.endpos );
		}
	}
}
float CalculateObjectStress( IPhysicsObject *pObject, CBaseEntity *pInputOwnerEntity, vphysics_objectstress_t *pOutput )
{
	CUtlVector< CBaseEntity * > pObjectList;
	CUtlVector< Vector >		objectForce;
	bool hasLargeObject = false;

	// add a slot for static objects
	pObjectList.AddToTail( NULL );
	objectForce.AddToTail( vec3_origin );
	// add a slot for friendly objects
	pObjectList.AddToTail( NULL );
	objectForce.AddToTail( vec3_origin );

	CBaseCombatCharacter *pBCC = pInputOwnerEntity->MyCombatCharacterPointer();

	IPhysicsFrictionSnapshot *pSnapshot = pObject->CreateFrictionSnapshot();
	float objMass = pObject->GetMass();
	while ( pSnapshot->IsValid() )
	{
		float force = pSnapshot->GetNormalForce();
		if ( force > 0.0f )
		{
			IPhysicsObject *pOther = pSnapshot->GetObject(1);
			CBaseEntity *pOtherEntity = static_cast<CBaseEntity *>(pOther->GetGameData());
			if ( !pOtherEntity )
			{
				// object was just deleted, but we still have a contact point this frame...
				// just assume it came from the world.
				pOtherEntity = GetWorldEntity();
			}
			CBaseEntity *pOtherOwner = pOtherEntity;
			if ( pOtherEntity->GetOwnerEntity() )
			{
				pOtherOwner = pOtherEntity->GetOwnerEntity();
			}

			int outIndex = 0;
			if ( !pOther->IsMoveable() )
			{
				outIndex = 0;
			}
			// NavIgnored objects are often being pushed by a friendly
			else if ( pBCC && (pBCC->IRelationType( pOtherOwner ) == D_LI || pOtherEntity->IsNavIgnored()) )
			{
				outIndex = 1;
			}
			// player held objects do no stress
			else if ( pOther->GetGameFlags() & FVPHYSICS_PLAYER_HELD )
			{
				outIndex = 1;
			}
			else
			{
				if ( pOther->GetMass() >= VPHYSICS_LARGE_OBJECT_MASS )
				{
					if ( pInputOwnerEntity->GetGroundEntity() != pOtherEntity)
					{
						hasLargeObject = true;
					}
				}
				// moveable, non-friendly
				
				// aggregate contacts over each object to avoid greater stress in multiple contact cases
				// NOTE: Contacts should be in order, so this shouldn't ever search, but just in case
				outIndex = pObjectList.Count();
				for ( int i = pObjectList.Count()-1; i >= 2; --i )
				{
					if ( pObjectList[i] == pOtherOwner )
					{
						outIndex = i;
						break;
					}
				}
				if ( outIndex == pObjectList.Count() )
				{
					pObjectList.AddToTail( pOtherOwner );
					objectForce.AddToTail( vec3_origin );
				}
			}

			if ( outIndex != 0 && pInputOwnerEntity->GetMoveType() != MOVETYPE_VPHYSICS && !IsPhysicallyControlled(pOtherEntity, pOther) )
			{
				// UNDONE: Test this!  This is to remove any shadow/shadow stress.  The game should handle this with blocked/damage
				force = 0.0f;
			}

			Vector normal;
			pSnapshot->GetSurfaceNormal( normal );
			objectForce[outIndex] += normal * force;
		}
		pSnapshot->NextFrictionData();
	}
	pObject->DestroyFrictionSnapshot( pSnapshot );
	pSnapshot = NULL;

	// clear out all friendly force
	objectForce[1].Init();

	float sum = 0;
	Vector negativeForce = vec3_origin;
	Vector positiveForce = vec3_origin;

	Assert( pObjectList.Count() == objectForce.Count() );
	for ( int objectIndex = pObjectList.Count()-1; objectIndex >= 0; --objectIndex )
	{
		sum += objectForce[objectIndex].Length();
		for ( int i = 0; i < 3; i++ )
		{
			if ( objectForce[objectIndex][i] < 0 )
			{
				negativeForce[i] -= objectForce[objectIndex][i];
			}
			else
			{
				positiveForce[i] += objectForce[objectIndex][i];
			}
		}
	}

	// "external" stress is two way (something pushes on the object and something else pushes back)
	// so the set of minimum values per component are the projections of the two-way force
	// "internal" stress is one way (the object is pushing against something OR something pushing back)
	// the momentum must have come from inside the object (gravity, controller, etc)
	Vector internalForce = vec3_origin;
	Vector externalForce = vec3_origin;

	for ( int i = 0; i < 3; i++ )
	{
		if ( negativeForce[i] < positiveForce[i] )
		{
			internalForce[i] = positiveForce[i] - negativeForce[i];
			externalForce[i] = negativeForce[i];
		}
		else
		{
			internalForce[i] = negativeForce[i] - positiveForce[i];
			externalForce[i] = positiveForce[i];
		}
	}

	// sum is kg in / s
	Vector gravVector;
	physenv->GetGravity( &gravVector );
	float gravity = gravVector.Length();
	if ( pInputOwnerEntity->GetMoveType() != MOVETYPE_VPHYSICS && pObject->IsMoveable() )
	{
		Vector lastVel;
		lastVel.Init();
		if ( pObject->GetShadowController() )
		{
			pObject->GetShadowController()->GetLastImpulse( &lastVel );
		}
		else 
		{
			if ( ( pObject->GetCallbackFlags() & CALLBACK_IS_PLAYER_CONTROLLER ) )
			{
				CBasePlayer *pPlayer = ToBasePlayer( pInputOwnerEntity );
				IPhysicsPlayerController *pController = pPlayer ? pPlayer->GetPhysicsController() : NULL;
				if ( pController )
				{
					pController->GetLastImpulse( &lastVel );
				}
			}
		}
		
		// Work in progress...

		// Peek into the controller for this object.  Look at the input velocity and make sure it's all
		// accounted for in the computed stress.  If not, redistribute external to internal as it's 
		// probably being reflected in a way we can't measure here.
		float inputLen = lastVel.Length() * (1.0f / physenv->GetSimulationTimestep()) * objMass;
		if ( inputLen > 0.0f )
		{
			float internalLen = internalForce.Length();
			if ( internalLen < inputLen )
			{
				float ratio = internalLen / inputLen;
				Vector delta = internalForce * (1.0f - ratio);
				internalForce += delta;
				float deltaLen = delta.Length();
				sum -= deltaLen;
				float extLen = VectorNormalize(externalForce) - deltaLen;
				if ( extLen < 0 )
				{
					extLen = 0;
				}
				externalForce *= extLen;
			}
		}
	}

	float invGravity = gravity;
	if ( invGravity <= 0 )
	{
		invGravity = 1.0f;
	}
	else
	{
		invGravity = 1.0f / invGravity;
	}
	sum *= invGravity;
	internalForce *= invGravity;
	externalForce *= invGravity;
	if ( !pObject->IsMoveable() )
	{
		// the above algorithm will see almost all force as internal if the object is not moveable 
		// (it doesn't push on anything else, so nothing is reciprocated)
		// exceptions for friction of a single other object with multiple contact points on this object
		
		// But the game wants to see it all as external because obviously the object can't move, so it can't have
		// internal stress
		externalForce = internalForce;
		internalForce.Init();

		if ( !pObject->IsStatic() )
		{
			sum += objMass;
		}
	}
	else
	{
		// assume object is at rest
		if ( sum > objMass )
		{
			sum = objMass + (sum-objMass) * 0.5;
		}
	}

	if ( pOutput )
	{
		pOutput->exertedStress = internalForce.Length();
		pOutput->receivedStress = externalForce.Length();
		pOutput->hasNonStaticStress = pObjectList.Count() > 2 ? true : false;
		pOutput->hasLargeObjectContact = hasLargeObject;
	}

	// sum is now kg 
	return sum;
}
Example #17
0
void CWeaponGravityGun::SoundUpdate( void )
{
	int newState;
	
	if ( m_hObject )
		newState = SS_LOCKEDON;
	else
		newState = SS_SCANNING;

	if ( newState != m_soundState )
	{
		SoundStop();
		m_soundState = newState;
		SoundStart();
	}

	switch( m_soundState )
	{
	case SS_SCANNING:
		break;
	case SS_LOCKEDON:
		{
			CPASAttenuationFilter filter( GetOwner() );
			filter.MakeReliable();

			float height = m_hObject->GetAbsOrigin().z - m_originalObjectPosition.z;

			// go from pitch 90 to 150 over a height of 500
			int pitch = 90 + (int)UTIL_LineFraction( height, 0, 500, 60 );

			CSoundParameters params;
			if ( GetParametersForSound( "Weapon_Physgun.LockedOn", params, NULL ) )
			{
				EmitSound_t ep( params );
				ep.m_nFlags = SND_CHANGE_VOL | SND_CHANGE_PITCH;
				ep.m_nPitch = pitch;

				EmitSound( filter, GetOwner()->entindex(), ep );
			}

			// attenutate the movement sounds over 200 units of movement
			float distance = UTIL_LineFraction( m_movementLength, 0, 200, 1.0 );

			// blend the "mass" sounds between 50 and 500 kg
			IPhysicsObject *pPhys = m_hObject->VPhysicsGetObject();
			
			float fade = UTIL_LineFraction( pPhys->GetMass(), 50, 500, 1.0 );

			if ( GetParametersForSound( "Weapon_Physgun.LightObject", params, NULL ) )
			{
				EmitSound_t ep( params );
				ep.m_nFlags = SND_CHANGE_VOL;
				ep.m_flVolume = fade * distance;

				EmitSound( filter, GetOwner()->entindex(), ep );
			}

			if ( GetParametersForSound( "Weapon_Physgun.HeavyObject", params, NULL ) )
			{
				EmitSound_t ep( params );
				ep.m_nFlags = SND_CHANGE_VOL;
				ep.m_flVolume = (1.0 - fade) * distance;

				EmitSound( filter, GetOwner()->entindex(), ep );
			}
		}
		break;
	}
}
//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CBaseHelicopter::DoRotorPhysicsPush( const Vector &vecRotorOrigin, float flAltitude )
{
	CBaseEntity *pEntity = NULL;
	trace_t tr;

	// First, trace down and find out where the was is hitting the ground
	UTIL_TraceLine( vecRotorOrigin, vecRotorOrigin+Vector(0,0,-flAltitude), (MASK_SOLID_BRUSHONLY|CONTENTS_WATER), NULL, COLLISION_GROUP_NONE, &tr );
	// Always raise the physics origin a bit
	Vector vecPhysicsOrigin = tr.endpos + Vector(0,0,64);

	// Debug
	if ( g_debug_basehelicopter.GetInt() == BASECHOPPER_DEBUG_WASH )
	{
		NDebugOverlay::Cross3D( vecPhysicsOrigin, -Vector(16,16,16), Vector(16,16,16), 0, 255, 255, true, 0.1f );
	}

	// Push entities that we've pushed before, and are still within range
	// Walk backwards because they may be removed if they're now out of range
	int iCount = m_hEntitiesPushedByWash.Count();
	bool bWasPushingObjects = (iCount > 0);
	for ( int i = (iCount-1); i >= 0; i-- )
	{
		if ( !DoWashPush( &(m_hEntitiesPushedByWash[i]), vecPhysicsOrigin ) )
		{
			// Out of range now, so remove
			m_hEntitiesPushedByWash.Remove(i);
		}
	}

	if ( m_flRotorWashEntitySearchTime > gpGlobals->curtime )
		return;

	// Any spare slots?
	iCount = m_hEntitiesPushedByWash.Count();
	if ( iCount >= BASECHOPPER_WASH_MAX_OBJECTS )
		return;

	// Find the lightest physics entity below us and add it to our list to push around
	CBaseEntity *pLightestEntity = NULL;
	float flLightestMass = 9999;
	while ((pEntity = gEntList.FindEntityInSphere(pEntity, vecPhysicsOrigin, BASECHOPPER_WASH_RADIUS )) != NULL)
	{
		IRotorWashShooter *pShooter = GetRotorWashShooter( pEntity );

		if ( pEntity->IsEFlagSet( EFL_NO_ROTORWASH_PUSH ))
			continue;

		if ( pShooter || pEntity->GetMoveType() == MOVETYPE_VPHYSICS || (pEntity->VPhysicsGetObject() && !pEntity->IsPlayer()) ) 
		{
			// Make sure it's not already in our wash
			bool bAlreadyPushing = false;
			for ( int i = 0; i < iCount; i++ )
			{
				if ( m_hEntitiesPushedByWash[i].hEntity == pEntity )
				{
					bAlreadyPushing = true;
					break;
				}
			}
			if ( bAlreadyPushing )
				continue;

			float flMass = FLT_MAX;
			if ( pShooter )
			{
				flMass = 1.0f;
			}
			else
			{
				// Don't try to push anything too big
				IPhysicsObject *pPhysObject = pEntity->VPhysicsGetObject();
				if ( pPhysObject )
				{
					flMass = pPhysObject->GetMass();
					if ( flMass > BASECHOPPER_WASH_MAX_MASS )
						continue;
				}
			}

			// Ignore anything bigger than the one we've already found
			if ( flMass > flLightestMass )
				continue;

			Vector vecSpot = pEntity->BodyTarget( vecPhysicsOrigin );

			// Don't push things too far below our starting point (helps reduce through-roof cases w/o doing a trace)
			if ( fabs( vecSpot.z - vecPhysicsOrigin.z ) > 96 )
				continue;

			Vector vecToSpot = ( vecSpot - vecPhysicsOrigin );
			vecToSpot.z = 0;
			float flDist = VectorNormalize( vecToSpot );
			if ( flDist > BASECHOPPER_WASH_RADIUS )
				continue;

			
			// Try to cast to the helicopter; if we can't, then we can't be hit.
			if ( pEntity->GetServerVehicle() )
			{
				UTIL_TraceLine( vecSpot, vecPhysicsOrigin, MASK_SOLID_BRUSHONLY, pEntity, COLLISION_GROUP_NONE, &tr );
				if ( tr.fraction != 1.0f )
					continue;
			}

			flLightestMass = flMass;
			pLightestEntity = pEntity;

			washentity_t Wash;
			Wash.hEntity = pLightestEntity;
			Wash.flWashStartTime = gpGlobals->curtime;
			m_hEntitiesPushedByWash.AddToTail( Wash );

			// Can we fit more after adding this one? No? Then we are done.
			iCount = m_hEntitiesPushedByWash.Count();
			if ( iCount >= BASECHOPPER_WASH_MAX_OBJECTS )
				break;
		}
	}

	// Handle sound.
	// If we just started pushing objects, ramp the blast sound up.
	if ( !bWasPushingObjects && m_hEntitiesPushedByWash.Count() )
	{
		if ( m_pRotorBlast )
		{
			CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
			controller.SoundChangeVolume( m_pRotorBlast, 1.0, 1.0 );
		}
	}
	else if ( bWasPushingObjects && m_hEntitiesPushedByWash.Count() == 0 )
	{
		if ( m_pRotorBlast )
		{
			// We just stopped pushing objects, so fade the blast sound out.
			CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
			controller.SoundChangeVolume( m_pRotorBlast, 0, 1.0 );
		}
	}
}
//-----------------------------------------------------------------------------
// Purpose: Push a physics object in our wash. Return false if it's now out of our wash
//-----------------------------------------------------------------------------
bool CBaseHelicopter::DoWashPush( washentity_t *pWash, const Vector &vecWashOrigin )
{
	if ( !pWash || !pWash->hEntity.Get() )
		return false;

	// Make sure the entity is still within our wash's radius
	CBaseEntity *pEntity = pWash->hEntity;

	// This can happen because we can dynamically turn this flag on and off
	if ( pEntity->IsEFlagSet( EFL_NO_ROTORWASH_PUSH ))
		return false;

	Vector vecSpot = pEntity->BodyTarget( vecWashOrigin );
	Vector vecToSpot = ( vecSpot - vecWashOrigin );
	vecToSpot.z = 0;
	float flDist = VectorNormalize( vecToSpot );
	if ( flDist > BASECHOPPER_WASH_RADIUS )
		return false;

	IRotorWashShooter *pShooter = GetRotorWashShooter( pEntity );
	IPhysicsObject *pPhysObject;

	
	float flPushTime = (gpGlobals->curtime - pWash->flWashStartTime);
	flPushTime = clamp( flPushTime, 0, BASECHOPPER_WASH_RAMP_TIME );
	float flWashAmount = RemapVal( flPushTime, 0, BASECHOPPER_WASH_RAMP_TIME, BASECHOPPER_WASH_PUSH_MIN, BASECHOPPER_WASH_PUSH_MAX );

	if ( pShooter )
	{
		Vector vecForce = (0.015f / 0.1f) * flWashAmount * vecToSpot * phys_pushscale.GetFloat();
		pEntity = pShooter->DoWashPush( pWash->flWashStartTime, vecForce );
		if ( !pEntity )
			return true;

		washentity_t Wash;
		Wash.hEntity = pEntity;
		Wash.flWashStartTime = pWash->flWashStartTime;
		int i = m_hEntitiesPushedByWash.AddToTail( Wash );
		pWash = &m_hEntitiesPushedByWash[i];
		
		pPhysObject = pEntity->VPhysicsGetObject();
		if ( !pPhysObject )
			return true;
	}
	else
	{
		// Airboat gets special treatment
		if ( FClassnameIs( pEntity, "prop_vehicle_airboat" ) )
		{
			DoWashPushOnAirboat( pEntity, vecToSpot, flWashAmount );
			return true;
		}

		pPhysObject = pEntity->VPhysicsGetObject();
		if ( !pPhysObject )
			return false;
	}

	// Push it away from the center of the wash
	float flMass = pPhysObject->GetMass();

	// This used to be mass independent, which is a bad idea because it blows 200kg engine blocks
	// as much as it blows cardboard and soda cans. Make this force mass-independent, but clamp at
	// 30kg. 
	flMass = MIN( flMass, 30.0f );

	Vector vecForce = (0.015f / 0.1f) * flWashAmount * flMass * vecToSpot * phys_pushscale.GetFloat();
	pEntity->VPhysicsTakeDamage( CTakeDamageInfo( this, this, vecForce, vecWashOrigin, flWashAmount, DMG_BLAST ) );

	// Debug
	if ( g_debug_basehelicopter.GetInt() == BASECHOPPER_DEBUG_WASH )
	{
		NDebugOverlay::Cross3D( pEntity->GetAbsOrigin(), -Vector(4,4,4), Vector(4,4,4), 255, 0, 0, true, 0.1f );
		NDebugOverlay::Line( pEntity->GetAbsOrigin(), pEntity->GetAbsOrigin() + vecForce, 255, 255, 0, true, 0.1f );

		IPhysicsObject *pPhysObject = pEntity->VPhysicsGetObject();
		Msg("Pushed %s (index %d) (mass %f) with force %f (min %.2f max %.2f) at time %.2f\n", 
			pEntity->GetClassname(), pEntity->entindex(), pPhysObject->GetMass(), flWashAmount, 
			BASECHOPPER_WASH_PUSH_MIN * flMass, BASECHOPPER_WASH_PUSH_MAX * flMass, gpGlobals->curtime );
	}

	// If we've pushed this thing for some time, remove it to give us a chance to find lighter things nearby
	if ( flPushTime > 2.0 )
		return false;

	return true;
}
Example #20
0
void PhysComputeSlideDirection( IPhysicsObject *pPhysics, const Vector &inputVelocity, const AngularImpulse &inputAngularVelocity, 
							   Vector *pOutputVelocity, Vector *pOutputAngularVelocity, float minMass )
{
	Vector velocity = inputVelocity;
	AngularImpulse angVel = inputAngularVelocity;
	Vector pos;

	IPhysicsFrictionSnapshot *pSnapshot = pPhysics->CreateFrictionSnapshot();
	while ( pSnapshot->IsValid() )
	{
		IPhysicsObject *pOther = pSnapshot->GetObject( 1 );
		if ( !pOther->IsMoveable() || pOther->GetMass() > minMass )
		{
			Vector normal;
			pSnapshot->GetSurfaceNormal( normal );

			// BUGBUG: Figure out the correct rotation clipping equation
			if ( pOutputAngularVelocity )
			{
				angVel = normal * DotProduct( angVel, normal );
#if 0
				pSnapshot->GetContactPoint( point );
				Vector point, dummy;
				AngularImpulse angularClip, clip2;

				pPhysics->CalculateVelocityOffset( normal, point, dummy, angularClip );
				VectorNormalize( angularClip );
				float proj = DotProduct( angVel, angularClip );
				if ( proj > 0 )
				{
					angVel -= angularClip * proj;
				}
				CrossProduct( angularClip, normal, clip2 );
				proj = DotProduct( angVel, clip2 );
				if ( proj > 0 )
				{
					angVel -= clip2 * proj;
				}
				//NDebugOverlay::Line( point, point - normal * 20, 255, 0, 0, true, 0.1 );
#endif
			}

			// Determine how far along plane to slide based on incoming direction.
			// NOTE: Normal points away from this object
			float proj = DotProduct( velocity, normal );
			if ( proj > 0.0f )
			{
				velocity -= normal * proj;
			}
		}
		pSnapshot->NextFrictionData();
	}
	pPhysics->DestroyFrictionSnapshot( pSnapshot );

	//NDebugOverlay::Line( pos, pos + unitVel * 20, 0, 0, 255, true, 0.1 );
	
	if ( pOutputVelocity )
	{
		*pOutputVelocity = velocity;
	}
	if ( pOutputAngularVelocity )
	{
		*pOutputAngularVelocity = angVel;
	}
}
Example #21
0
CBaseEntity *CreateServerRagdoll( CBaseAnimating *pAnimating, int forceBone, const CTakeDamageInfo &info, int collisionGroup, bool bUseLRURetirement )
{
	SyncAnimatingWithPhysics( pAnimating );

	CRagdollProp *pRagdoll = (CRagdollProp *)CBaseEntity::CreateNoSpawn( "prop_ragdoll", pAnimating->GetAbsOrigin(), vec3_angle, NULL );
	pRagdoll->CopyAnimationDataFrom( pAnimating );
	pRagdoll->SetOwnerEntity( pAnimating );

	pRagdoll->InitRagdollAnimation();
	matrix3x4_t pBoneToWorld[MAXSTUDIOBONES], pBoneToWorldNext[MAXSTUDIOBONES];
	
	const float dt = 0.1f;

	// Copy over dissolve state...
	if ( pAnimating->IsEFlagSet( EFL_NO_DISSOLVE ) )
	{
		pRagdoll->AddEFlags( EFL_NO_DISSOLVE );
	}

	// NOTE: This currently is only necessary to prevent manhacks from
	// colliding with server ragdolls they kill
	pRagdoll->SetKiller( info.GetInflictor() );
	pRagdoll->SetSourceClassName( pAnimating->GetClassname() );

	// NPC_STATE_DEAD npc's will have their COND_IN_PVS cleared, so this needs to force SetupBones to happen
	unsigned short fPrevFlags = pAnimating->GetBoneCacheFlags();
	pAnimating->SetBoneCacheFlags( BCF_NO_ANIMATION_SKIP );

	// UNDONE: Extract velocity from bones via animation (like we do on the client)
	// UNDONE: For now, just move each bone by the total entity velocity if set.
	pAnimating->SetupBones( pBoneToWorld, BONE_USED_BY_ANYTHING );

	// Reset previous bone flags
	pAnimating->ClearBoneCacheFlags( BCF_NO_ANIMATION_SKIP );
	pAnimating->SetBoneCacheFlags( fPrevFlags );

	memcpy( pBoneToWorldNext, pBoneToWorld, sizeof(pBoneToWorld) );
	Vector vel = pAnimating->GetAbsVelocity();
	if ( vel.LengthSqr() > 0 )
	{
		int numbones = pAnimating->GetModelPtr()->numbones();
		for ( int i = 0; i < numbones; i++ )
		{
			Vector pos;
			MatrixGetColumn( pBoneToWorldNext[i], 3, pos );
			pos += vel * dt;
			MatrixSetColumn( pos, 3, pBoneToWorldNext[i] );
		}
	}

	// Is this a vehicle / NPC collision?
	if ( (info.GetDamageType() & DMG_VEHICLE) && pAnimating->MyNPCPointer() )
	{
		// init the ragdoll with no forces
		pRagdoll->InitRagdoll( vec3_origin, -1, vec3_origin, pBoneToWorld, pBoneToWorldNext, dt, collisionGroup, true );

		// apply vehicle forces
		// Get a list of bones with hitboxes below the plane of impact
		int boxList[128];
		Vector normal(0,0,-1);
		int count = pAnimating->GetHitboxesFrontside( boxList, ARRAYSIZE(boxList), normal, DotProduct( normal, info.GetDamagePosition() ) );
		
		// distribute force over mass of entire character
		float massScale = Studio_GetMass(pAnimating->GetModelPtr());
		massScale = clamp( massScale, 1, 1e4 );
		massScale = 1 / massScale;

		// distribute the force
		// BUGBUG: This will hit the same bone twice if it has two hitboxes!!!!
		ragdoll_t *pRagInfo = pRagdoll->GetRagdoll();
		for ( int i = 0; i < count; i++ )
		{
			int physBone = pAnimating->GetPhysicsBone( pAnimating->GetHitboxBone( boxList[i] ) );
			IPhysicsObject *pPhysics = pRagInfo->list[physBone].pObject;
			pPhysics->ApplyForceCenter( info.GetDamageForce() * pPhysics->GetMass() * massScale );
		}
	}
	else
	{
		pRagdoll->InitRagdoll( info.GetDamageForce(), forceBone, info.GetDamagePosition(), pBoneToWorld, pBoneToWorldNext, dt, collisionGroup, true );
	}

	// Are we dissolving?
	if ( pAnimating->IsDissolving() )
	{
		pRagdoll->TransferDissolveFrom( pAnimating );
	}
	else if ( bUseLRURetirement )
	{
		pRagdoll->AddSpawnFlags( SF_RAGDOLLPROP_USE_LRU_RETIREMENT );
		s_RagdollLRU.MoveToTopOfLRU( pRagdoll );
	}

	// Tracker 22598:  If we don't set the OBB mins/maxs to something valid here, then the client will have a zero sized hull
	//  for the ragdoll for one frame until Vphysics updates the real obb bounds after the first simulation frame.  Having
	//  a zero sized hull makes the ragdoll think it should be faded/alpha'd to zero for a frame, so you get a blink where
	//  the ragdoll doesn't draw initially.
	Vector mins, maxs;
	mins = pAnimating->CollisionProp()->OBBMins();
	maxs = pAnimating->CollisionProp()->OBBMaxs();
	pRagdoll->CollisionProp()->SetCollisionBounds( mins, maxs );

	return pRagdoll;
}
Vector QUA_helicopter::CalcDamageForceVector( const CTakeDamageInfo &info )
{
	// Already have a damage force in the data, use that.
	if (info.GetDamageForce() != vec3_origin || (info.GetDamageType() & /*DMG_NO_PHYSICS_FORCE*/DMG_BLAST))
	{
		//if( info.GetDamageType() & DMG_BLAST )
		//{
			// Fudge blast forces a little bit, so that each
			// victim gets a slightly different trajectory. 
			// This simulates features that usually vary from
			// person-to-person variables such as bodyweight,
			// which are all indentical for characters using the same model.
			float scale = random->RandomFloat( 0.85, 1.15 );
			Vector force = info.GetDamageForce();
			force.x *= scale;
			force.y *= scale;
			// Try to always exaggerate the upward force because we've got pretty harsh gravity
			force.z *= (force.z > 0) ? 1.15 : scale;
			return force;
		//}

		return info.GetDamageForce();
	}

	CBaseEntity *pForce = info.GetInflictor();
	if ( !pForce )
	{
		pForce = info.GetAttacker();
	}

	if ( pForce )
	{
		// Calculate an impulse large enough to push a 75kg man 4 in/sec per point of damage
		float forceScale = info.GetDamage() * 75 * 4;

		Vector forceVector;
		// If the damage is a blast, point the force vector higher than usual, this gives 
		// the ragdolls a bodacious "really got blowed up" look.
		if( info.GetDamageType() & DMG_BLAST )
		{
			// exaggerate the force from explosions a little (37.5%)
			forceVector = (GetLocalOrigin() + Vector(0, 0, WorldAlignSize().z) ) - pForce->GetLocalOrigin();
			VectorNormalize(forceVector);
			forceVector *= 1.375f;
		}
		else
		{
			// taking damage from self?  Take a little random force, but still try to collapse on the spot.
			if ( this == pForce )
			{
				forceVector.x = random->RandomFloat( -1.0f, 1.0f );
				forceVector.y = random->RandomFloat( -1.0f, 1.0f );
				forceVector.z = 0.0;
				forceScale = random->RandomFloat( 1000.0f, 2000.0f );
			}
			else
			{
				// UNDONE: Collision forces are baked in to CTakeDamageInfo now
				// UNDONE: Is this MOVETYPE_VPHYSICS code still necessary?
				if ( pForce->GetMoveType() == MOVETYPE_VPHYSICS )
				{
					// killed by a physics object
					IPhysicsObject *pPhysics = VPhysicsGetObject();
					if ( !pPhysics )
					{
						pPhysics = pForce->VPhysicsGetObject();
					}
					pPhysics->GetVelocity( &forceVector, NULL );
					forceScale = pPhysics->GetMass();
				}
				else
				{
					forceVector = GetLocalOrigin() - pForce->GetLocalOrigin();
					VectorNormalize(forceVector);
				}
			}
		}
		return forceVector * forceScale;
	}
	return vec3_origin;
}
Example #23
0
//---------------------------------------------------------
//---------------------------------------------------------
void CBounceBomb::SettleThink()
{
	SetNextThink( gpGlobals->curtime + 0.05 );
	StudioFrameAdvance();

	if( GetParent() )
	{
		// A scanner or something is carrying me. Just keep checking back.
		return;
	}

	// Not being carried.
	if( !VPhysicsGetObject() )
	{
		// Probably was just dropped. Get physics going.
		CreateVPhysics();

		if( !VPhysicsGetObject() )
		{
			Msg("**** Can't create vphysics for combine_mine!\n" );
			UTIL_Remove( this );
			return;
		}

		VPhysicsGetObject()->Wake();
		return;
	}

	if( !m_bDisarmed )
	{
		if( VPhysicsGetObject()->IsAsleep() && !(VPhysicsGetObject()->GetGameFlags() & FVPHYSICS_PLAYER_HELD) )
		{
			// If i'm not resting on the world, jump randomly.
			trace_t tr;
			UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() - Vector( 0, 0, 1024 ), MASK_SHOT|CONTENTS_GRATE, this, COLLISION_GROUP_NONE, &tr );

			bool bHop = false;
			if( tr.m_pEnt )
			{
				IPhysicsObject *pPhysics = tr.m_pEnt->VPhysicsGetObject();

				if( pPhysics && pPhysics->GetMass() <= 1000 )
				{
					// Light physics objects can be moved out from under the mine.
					bHop = true;
				}
				else if( tr.m_pEnt->m_takedamage != DAMAGE_NO )
				{
					// Things that can be harmed can likely be broken.
					bHop = true;
				}

				if( bHop )
				{
					Vector vecForce;
					vecForce.x = random->RandomFloat( -1000, 1000 );
					vecForce.y = random->RandomFloat( -1000, 1000 );
					vecForce.z = 2500;

					AngularImpulse torque( 160, 0, 160 );

					Flip( vecForce, torque );
					return;
				}

				// Check for upside-down
				Vector vecUp;
				GetVectors( NULL, NULL, &vecUp );
				if( vecUp.z <= 0.8 )
				{
					// Landed upside down. Right self
					Vector vecForce( 0, 0, 2500 );
					Flip( vecForce, AngularImpulse( 60, 0, 0 ) );
					return;
				}
			}

			// Check to make sure I'm not in a forbidden location
			if( !IsValidLocation() )
			{
				return;
			}

			// Lock to what I'm resting on
			constraint_ballsocketparams_t ballsocket;
			ballsocket.Defaults();
			ballsocket.constraint.Defaults();
			ballsocket.constraint.forceLimit = lbs2kg(1000);
			ballsocket.constraint.torqueLimit = lbs2kg(1000);
			ballsocket.InitWithCurrentObjectState( g_PhysWorldObject, VPhysicsGetObject(), GetAbsOrigin() );
			m_pConstraint = physenv->CreateBallsocketConstraint( g_PhysWorldObject, VPhysicsGetObject(), NULL, ballsocket );
			CloseHooks();

			SetMineState( MINE_STATE_ARMED );
		}
	}
}
//-----------------------------------------------------------------------------
// Purpose: 
//
//
// Output : 
//-----------------------------------------------------------------------------
CBaseEntity *CNPC_RollerDozer::FindDebris( void )
{
	if( !m_pHintNode )
	{
		// Detect rubbish near a hint node.
		CAI_Hint* pHintNode = CAI_Hint::FindHint( this, HINT_ROLLER_CLEANUP_POINT, 0, 1024 ); 


		if( pHintNode)
		{
			// Search around the hint node for debris that should be cleared.
			Vector vecHintNodeOrigin;
			
			// Get hint node position
			vecHintNodeOrigin;
			pHintNode->GetPosition(this,&vecHintNodeOrigin);

			CBaseEntity *pList[ 16 ];
			Vector vecDeltaUp( 200, 200, 64 );
			Vector vecDeltaDown( 200, 200, 10 );
			int i;
			IPhysicsObject	*pPhysObj;

			int count = UTIL_EntitiesInBox( pList, 16, vecHintNodeOrigin - vecDeltaDown, vecHintNodeOrigin + vecDeltaUp, 0 );
			
			float m_flHeaviestMass = 0;
			CBaseEntity *pHeaviest = NULL;
			
			for( i = 0 ; i < count ; i++ )
			{
				pPhysObj = pList[ i ]->VPhysicsGetObject();

				if( !pPhysObj || FClassnameIs( pList[ i ], "npc_rollerdozer" ) )
				{
					// Only consider physics objects. Exclude rollers.
					continue;
				}

				if( pPhysObj->GetMass() <= 400 )
				{
					if( pPhysObj->GetMass() > m_flHeaviestMass )
					{
						m_flHeaviestMass = pPhysObj->GetMass();
						pHeaviest = pList[ i ];
					}

/*
					// Report to the cleanup point and doze this piece of debris away.
					SetCondition( COND_ROLLERDOZER_FOUND_DEBRIS );
					m_vecCleanupPoint = vecHintNodeOrigin;


					return pList[ i ];
*/
				}
			}

			if( pHeaviest )
			{
				SetCondition( COND_ROLLERDOZER_FOUND_DEBRIS );
				//NDebugOverlay::Line( GetLocalOrigin(), pHeaviest->GetLocalOrigin(), 255,255,0, true, 3 );
				m_vecCleanupPoint = vecHintNodeOrigin;
				return pHeaviest;
			}
		}
	}

	return NULL;
}
void CNPC_Roller::Unstick( void )
{
	CBaseEntity		*pList[ 16 ];
	IPhysicsObject	*pPhysObj;
	int				i;
	Vector			vecDirToEnemy;
	Vector			vecDirToObject;

	m_flWaitFinished = gpGlobals->curtime;

	int count = UTIL_EntitiesInBox( pList, 16, GetAbsOrigin() - vecDelta, GetAbsOrigin() + vecDelta, 0 );

	m_vecUnstickDirection = vec3_origin;

	for( i = 0 ; i < count ; i++ )
	{
		pPhysObj = pList[ i ]->VPhysicsGetObject();

		if( !pPhysObj || pList[ i ]->m_iClassname == m_iClassname )
		{
			// Only consider physics objects. Exclude rollers.
			continue;
		}

		if( pPhysObj->GetMass() <= ROLLER_MAX_PUSH_MASS )
		{
			// Try to bash this physics object.
			trace_t tr;

			AI_TraceLine( GetAbsOrigin(), pList[ i ]->GetAbsOrigin(), MASK_SHOT, this, COLLISION_GROUP_NONE, &tr ); 

			if( tr.fraction == 1.0 || tr.m_pEnt == pList[ i ] )
			{
				// Roll towards this item if the trace hits nothing, or
				// the trace hits the object.
				Vector vecBashDir;

				vecBashDir = pList[ i ]->GetAbsOrigin() - GetAbsOrigin();
				VectorNormalize( vecBashDir );
				vecBashDir.z = 0.0;

				//NDebugOverlay::Line( GetAbsOrigin(), pList[ i ]->GetAbsOrigin(), 0,255,0, true, 2 );

				m_vecUnstickDirection = vecBashDir * 80;

				return;
			}
		}
	}

	// No physics objects. Just pick a direction with some clearance and go there.

#define ROLLER_UNSTICK_DIST 80
	Vector vecDirections[ 4 ] = 
	{
		Vector(  0,	ROLLER_UNSTICK_DIST, 0 ),
		Vector(  ROLLER_UNSTICK_DIST,	0, 0 ),
		Vector(  0, -ROLLER_UNSTICK_DIST, 0 ),
		Vector( -ROLLER_UNSTICK_DIST,  0, 0 )
	};

	trace_t tr;

	for( i = 0 ; i < 4 ; i++ )
	{
		AI_TraceLine( GetAbsOrigin(), GetAbsOrigin() + vecDirections[ i ], MASK_SHOT, this, COLLISION_GROUP_NONE, &tr ); 

		if( tr.fraction == 1.0 )
		{
			m_vecUnstickDirection = vecDirections[ i ];

			//NDebugOverlay::Line( GetLocalOrigin(), GetLocalOrigin() + m_vecUnstickDirection, 255,255,0, true, 2 );

			// Roll in this direction for a couple of seconds.
			Msg( "unsticking!\n" );
			return;
		}
	}
}
//=========================================================
// SonicAttack
//=========================================================
void CNPC_Houndeye::SonicAttack ( void )
{
	EmitSound( "NPC_Houndeye.SonicAttack" );

	float		flAdjustedDamage;
	float		flDist;

	CBroadcastRecipientFilter filter2;
	te->BeamRingPoint(filter2, 0.0,
		GetAbsOrigin(),							//origin
		16,										//start radius
		HOUNDEYE_MAX_ATTACK_RADIUS,//end radius
		m_iSpriteTexture,						//texture
		0,										//halo index
		0,										//start frame
		0,										//framerate
		0.2,									//life
		24,									//width
		16,										//spread
		0,										//amplitude
		188,									//r
		220,									//g
		255,									//b
		192,									//a
		0										//speed
		);

	CBroadcastRecipientFilter filter3;
	te->BeamRingPoint(filter3, 0.0,
		GetAbsOrigin(),									//origin
		16,												//start radius
		HOUNDEYE_MAX_ATTACK_RADIUS / 2,											//end radius
		m_iSpriteTexture,								//texture
		0,												//halo index
		0,												//start frame
		0,												//framerate
		0.2,											//life
		24,											//width
		16,												//spread
		0,												//amplitude
		188,											//r
		220,											//g
		255,											//b
		192,											//a
		0												//speed
		);

	CBaseEntity *pEntity = NULL;
	// iterate on all entities in the vicinity.
	while ((pEntity = gEntList.FindEntityInSphere(pEntity, GetAbsOrigin(), HOUNDEYE_MAX_ATTACK_RADIUS)) != NULL)
	{
		if (pEntity->m_takedamage != DAMAGE_NO)
		{
			if (!FClassnameIs(pEntity, "npc_houndeye"))
			{// houndeyes don't hurt other houndeyes with their attack

				// houndeyes do FULL damage if the ent in question is visible. Half damage otherwise.
				// This means that you must get out of the houndeye's attack range entirely to avoid damage.
				// Calculate full damage first

				flAdjustedDamage = sk_Houndeye_dmg_blast.GetFloat();

				flDist = (pEntity->WorldSpaceCenter() - GetAbsOrigin()).Length();

				flAdjustedDamage -= (flDist / HOUNDEYE_MAX_ATTACK_RADIUS) * flAdjustedDamage;

				if (!FVisible(pEntity))
				{
					if (pEntity->IsPlayer())
					{
						// if this entity is a client, and is not in full view, inflict half damage. We do this so that players still 
						// take the residual damage if they don't totally leave the houndeye's effective radius. We restrict it to clients
						// so that monsters in other parts of the level don't take the damage and get pissed.
						flAdjustedDamage *= 0.5;
					}
					else if (!FClassnameIs(pEntity, "func_breakable") && !FClassnameIs(pEntity, "func_pushable"))
					{
						// do not hurt nonclients through walls, but allow damage to be done to breakables
						flAdjustedDamage = 0;
					}
				}

				//ALERT ( at_aiconsole, "Damage: %f\n", flAdjustedDamage );

				if (flAdjustedDamage > 0)
				{
					CTakeDamageInfo info(this, this, flAdjustedDamage, DMG_SONIC | DMG_ALWAYSGIB);
					CalculateExplosiveDamageForce(&info, (pEntity->GetAbsOrigin() - GetAbsOrigin()), pEntity->GetAbsOrigin());

					pEntity->TakeDamage(info);

					if ((pEntity->GetAbsOrigin() - GetAbsOrigin()).Length2D() <= HOUNDEYE_MAX_ATTACK_RADIUS)
					{
						if (pEntity->GetMoveType() == MOVETYPE_VPHYSICS || (pEntity->VPhysicsGetObject() && !pEntity->IsPlayer()))
						{
							IPhysicsObject *pPhysObject = pEntity->VPhysicsGetObject();

							if (pPhysObject)
							{
								float flMass = pPhysObject->GetMass();

								if (flMass <= HOUNDEYE_TOP_MASS)
								{
									// Increase the vertical lift of the force
									Vector vecForce = info.GetDamageForce();
									vecForce.z *= 2.0f;
									info.SetDamageForce(vecForce);

									pEntity->VPhysicsTakeDamage(info);
								}
							}
						}
					}
				}
			}
		}
	}
}
Example #27
0
//-----------------------------------------------------------------------------
// Purpose: Pulls physical objects towards the vortex center, killing them if they come too near
//-----------------------------------------------------------------------------
void CGravityVortexController::PullThink( void )
{
	// Pull any players close enough to us
	PullPlayersInRange();

	Vector mins, maxs;
	mins = GetAbsOrigin() - Vector( m_flRadius, m_flRadius, m_flRadius );
	maxs = GetAbsOrigin() + Vector( m_flRadius, m_flRadius, m_flRadius );

	// Draw debug information
	if ( g_debug_hopwire.GetBool() )
	{
		NDebugOverlay::Box( GetAbsOrigin(), mins - GetAbsOrigin(), maxs - GetAbsOrigin(), 0, 255, 0, 16, 4.0f );
	}

	CBaseEntity *pEnts[128];
	int numEnts = UTIL_EntitiesInBox( pEnts, 128, mins, maxs, 0 );

	for ( int i = 0; i < numEnts; i++ )
	{
		IPhysicsObject *pPhysObject = NULL;

		// Attempt to kill and ragdoll any victims in range
		if ( KillNPCInRange( pEnts[i], &pPhysObject ) == false )
		{	
			// If we didn't have a valid victim, see if we can just get the vphysics object
			pPhysObject = pEnts[i]->VPhysicsGetObject();
			if ( pPhysObject == NULL )
				continue;
		}

		float mass;

		CRagdollProp *pRagdoll = dynamic_cast< CRagdollProp* >( pEnts[i] );
		if ( pRagdoll != NULL )
		{
			ragdoll_t *pRagdollPhys = pRagdoll->GetRagdoll();
			mass = 0.0f;
			
			// Find the aggregate mass of the whole ragdoll
			for ( int j = 0; j < pRagdollPhys->listCount; ++j )
			{
				mass += pRagdollPhys->list[j].pObject->GetMass();
			}
		}
		else
		{
			mass = pPhysObject->GetMass();
		}

		Vector	vecForce = GetAbsOrigin() - pEnts[i]->WorldSpaceCenter();
		Vector	vecForce2D = vecForce;
		vecForce2D[2] = 0.0f;
		float	dist2D = VectorNormalize( vecForce2D );
		float	dist = VectorNormalize( vecForce );
		
		// FIXME: Need a more deterministic method here
		if ( dist < 48.0f )
		{
			ConsumeEntity( pEnts[i] );
			continue;
		}

		// Must be within the radius
		if ( dist > m_flRadius )
			continue;

		// Find the pull force
		vecForce *= ( 1.0f - ( dist2D / m_flRadius ) ) * m_flStrength * mass;
		
		if ( pEnts[i]->VPhysicsGetObject() )
		{
			// Pull the object in
			pEnts[i]->VPhysicsTakeDamage( CTakeDamageInfo( this, this, vecForce, GetAbsOrigin(), m_flStrength, DMG_BLAST ) );
		}
	}

	// Keep going if need-be
	if ( m_flEndTime > gpGlobals->curtime )
	{
		SetThink( &CGravityVortexController::PullThink );
		SetNextThink( gpGlobals->curtime + 0.1f );
	}
	else
	{
		//Msg( "Consumed %.2f kilograms\n", m_flMass );
		//CreateDenseBall();
	}
}
bool CNPC_Dog::FindPhysicsObject( const char *pPickupName, CBaseEntity *pIgnore )
{
	CBaseEntity		*pEnt = NULL;
	CBaseEntity		*pNearest = NULL;
	float			flDist;
	IPhysicsObject	*pPhysObj = NULL;
	float			flNearestDist = 99999;

	if ( pPickupName != NULL && strlen( pPickupName ) > 0 )
	{
		pEnt = gEntList.FindEntityByName( NULL, pPickupName );
		
		if ( m_hUnreachableObjects.Find( pEnt ) == -1  )
		{
			m_bHasObject = false;
			m_hPhysicsEnt = pEnt;
			return true;
		}
	}
	
	while ( ( pEnt = gEntList.FindEntityByClassname( pEnt, "prop_physics" ) ) != NULL )
	{
		//We don't want this one.
		if ( pEnt == pIgnore )
			 continue;

		if ( m_hUnreachableObjects.Find( pEnt ) != -1 )
			 continue;

		pPhysObj = pEnt->VPhysicsGetObject();

		if( pPhysObj == NULL )
			continue;

		if ( pPhysObj->GetMass() > DOG_MAX_THROW_MASS )
			 continue;
		
		Vector center = pEnt->WorldSpaceCenter();
		flDist = UTIL_DistApprox2D( GetAbsOrigin(), center );

		vcollide_t *pCollide = modelinfo->GetVCollide( pEnt->GetModelIndex() );

		if ( pCollide == NULL )
			 continue;

		if ( pPhysObj->GetGameFlags() & FVPHYSICS_PLAYER_HELD )
			 continue;

		if ( pPhysObj->IsMoveable() == false )
			 continue;

		if ( pEnt->GetCollisionGroup() == COLLISION_GROUP_DEBRIS || 
			 pEnt->GetCollisionGroup() == COLLISION_GROUP_INTERACTIVE_DEBRIS )
			 continue;

		if ( center.z > EyePosition().z )
			 continue;

		if ( flDist >= flNearestDist )
			 continue;

		if ( FVisible( pEnt ) == false )
			 continue;
		
		pNearest = pEnt;
		flNearestDist = flDist;
	}

	m_bHasObject = false;
	m_hPhysicsEnt = pNearest;

	if ( dog_debug.GetBool() == true )
	{
		if ( pNearest )
			 NDebugOverlay::Box( pNearest->WorldSpaceCenter(), pNearest->CollisionProp()->OBBMins(), pNearest->CollisionProp()->OBBMaxs(), 255, 0, 255, true, 3 );
	}

	if( m_hPhysicsEnt == NULL )
	{
		return false;
	}
	else
	{
		return true;
	}
}
Example #29
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 = NULL;
		
		//Find the real object we hit.
		if( tr.physicsbone >= 0 )
		{
			if ( pModel->m_pRagdoll )
			{
				CRagdoll *pCRagdoll = dynamic_cast < CRagdoll * > ( pModel->m_pRagdoll );

				if ( pCRagdoll )
				{
					ragdoll_t *pRagdollT = pCRagdoll->GetRagdoll();

					if ( tr.physicsbone < pRagdollT->listCount )
					{
						pPhysicsObject = pRagdollT->list[tr.physicsbone].pObject;
					}
				}
			}
		}

		if ( pPhysicsObject == NULL )
			return ITERATION_CONTINUE;

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

			if ( pReference == NULL || pPhysicsObject == NULL )
				 return ITERATION_CONTINUE;
			
			float flMass = pPhysicsObject->GetMass();
			pPhysicsObject->SetMass( flMass * 2 );

			constraint_ballsocketparams_t ballsocket;
			ballsocket.Defaults();
		
			pReference->WorldToLocal( &ballsocket.constraintPosition[0], m_vWorld );
			pPhysicsObject->WorldToLocal( &ballsocket.constraintPosition[1], tr.endpos );
	
			physenv->CreateBallsocketConstraint( pReference, pPhysicsObject, NULL, ballsocket );

			//Play a sound
			CPASAttenuationFilter filter( pEnt );

			EmitSound_t ep;
			ep.m_nChannel = CHAN_VOICE;
			ep.m_pSoundName =  "Weapon_Crossbow.BoltSkewer";
			ep.m_flVolume = 1.0f;
			ep.m_SoundLevel = SNDLVL_NORM;
			ep.m_pOrigin = &pEnt->GetAbsOrigin();

			C_BaseEntity::EmitSound( filter, SOUND_FROM_WORLD, ep );
	
			return ITERATION_STOP;
		}

		return ITERATION_CONTINUE;
	}
void CNPC_Dog::ThrowObject( const char *pAttachmentName )
{
	if ( m_hPhysicsEnt )
	{
		m_bHasObject = false;

		IPhysicsObject *pPhysObj = m_hPhysicsEnt->VPhysicsGetObject();

		if ( pPhysObj )
		{
			Vector vGunPos;
			QAngle angGunAngles;

			AngularImpulse angVelocity = RandomAngularImpulse( -250 , -250 ) / pPhysObj->GetMass();

			InvalidateBoneCache();

			int iAttachment = LookupAttachment( pAttachmentName );

			if ( iAttachment == 0 )
				 iAttachment = m_iPhysGunAttachment;
			
			GetAttachment( iAttachment, vGunPos, angGunAngles );

			pPhysObj->Wake();

			if ( pPhysObj->GetShadowController() )
			{
				m_hPhysicsEnt->SetParent( NULL );
				m_hPhysicsEnt->SetMoveType( (MoveType_t)m_iContainerMoveType );
				m_hPhysicsEnt->SetOwnerEntity( this );

				pPhysObj->RemoveShadowController();
				pPhysObj->SetPosition( m_hPhysicsEnt->GetLocalOrigin(), m_hPhysicsEnt->GetLocalAngles(), true );

				pPhysObj->RecheckCollisionFilter();
				pPhysObj->RecheckContactPoints();
			}
				
			if ( m_hThrowTarget == NULL )
			#ifdef SecobMod__Enable_Fixed_Multiplayer_AI
				m_hThrowTarget = UTIL_GetNearestVisiblePlayer(this); 
			#else
				m_hThrowTarget = AI_GetSinglePlayer();
			#endif //SecobMod__Enable_Fixed_Multiplayer_AI

			Vector vThrowDirection;

			if ( m_hThrowTarget )
			{
				Vector vThrowOrigin = m_hThrowTarget->GetAbsOrigin();
				
				if ( m_hThrowTarget->IsPlayer() )
					 vThrowOrigin = vThrowOrigin + Vector( random->RandomFloat( -128, 128 ), random->RandomFloat( -128, 128 ), 0 );

				Vector vecToss = VecCheckToss( this, vGunPos, vThrowOrigin, m_flThrowArcModifier, 1.0f, true );

				if( vecToss == vec3_origin )
				{
					// Fix up an impossible throw so dog will at least toss the box in the target's general direction instead of dropping it.
					// Also toss it up in the air so it will fall down and break. (Just throw the box up at a 45 degree angle)
					Vector forward, up;
					GetVectors( &forward, NULL, &up );

					vecToss = forward + up;
					VectorNormalize( vecToss );

					vecToss *= pPhysObj->GetMass() * 30.0f;
				}

				vThrowDirection = vecToss + ( m_hThrowTarget->GetSmoothedVelocity() / 2 );
							
				Vector vLinearDrag;

				Vector unitVel = vThrowDirection;
				VectorNormalize( unitVel );

				float flTest = 1000 / vThrowDirection.Length();

				float flDrag = pPhysObj->CalculateLinearDrag( vThrowDirection );
				vThrowDirection = vThrowDirection + ( unitVel * ( flDrag * flDrag ) ) / flTest;
			
				pPhysObj->SetVelocity( &vThrowDirection, &angVelocity );
				
				m_flTimeToCatch = gpGlobals->curtime + dog_max_wait_time.GetFloat();

				//Don't start pulling until the object is away from me.
				//We base the time on the throw velocity.
				m_flTimeToPull = gpGlobals->curtime + ( 1000 / vThrowDirection.Length() );
			}

			//Fire Output!
			m_OnThrow.FireOutput( this, this );

			ClearBeams();
			
			if ( m_bBeamEffects == true )
			{
				EmitSound( "Weapon_PhysCannon.Launch" );
				
				CBeam *pBeam = CBeam::BeamCreate(  "sprites/orangelight1.vmt", 1.8 );

				if ( pBeam != NULL )
				{
					pBeam->PointEntInit( m_hPhysicsEnt->WorldSpaceCenter(), this );
					pBeam->SetEndAttachment( m_iPhysGunAttachment );
					pBeam->SetWidth( 6.4 );
					pBeam->SetEndWidth( 12.8 );					
					pBeam->SetBrightness( 255 );
					pBeam->SetColor( 255, 255, 255 );
					pBeam->LiveForTime( 0.2f );
					pBeam->RelinkBeam();
					pBeam->SetNoise( 2 );
				}
			
				Vector	shotDir = ( m_hPhysicsEnt->WorldSpaceCenter() - vGunPos );
				VectorNormalize( shotDir );

				CPVSFilter filter( m_hPhysicsEnt->WorldSpaceCenter() );
				te->GaussExplosion( filter, 0.0f, m_hPhysicsEnt->WorldSpaceCenter() - ( shotDir * 4.0f ), RandomVector(-1.0f, 1.0f), 0 );
			}
		}
	}
}