bool CBoneFollower::Init( CBaseEntity *pOwner, const char *pModelName, solid_t &solid, const Vector &position, const QAngle &orientation )
{
	SetOwnerEntity( pOwner );
	UTIL_SetModel( this, pModelName );

	m_fEffects |= EF_NODRAW; // invisible
#if VISUALIZE_FOLLOWERS_BOUNDINGBOX
	m_debugOverlays |= OVERLAY_BBOX_BIT;
#endif

	m_modelIndex = modelinfo->GetModelIndex( pModelName );
	m_solidIndex = solid.index;
	SetAbsOrigin( position );
	SetAbsAngles( orientation );
	SetMoveType( MOVETYPE_PUSH );
	SetSolid( SOLID_VPHYSICS );
	SetCollisionGroup( pOwner->GetCollisionGroup() );
	AddSolidFlags( FSOLID_CUSTOMRAYTEST | FSOLID_CUSTOMBOXTEST );
	solid.params.pGameData = (void *)this;
	IPhysicsObject *pPhysics = VPhysicsInitShadow( false, false, &solid );
	if ( !pPhysics )
		return false;

	pPhysics->SetCallbackFlags( pPhysics->GetCallbackFlags() | CALLBACK_GLOBAL_TOUCH );
	pPhysics->EnableGravity( false );

	return true;
}
bool CBoneFollower::Init( CBaseEntity *pOwner, const char *pModelName, solid_t &solid, const Vector &position, const QAngle &orientation )
{
	SetOwnerEntity( pOwner );
	UTIL_SetModel( this, pModelName );

	AddEffects( EF_NODRAW ); // invisible

	m_modelIndex = modelinfo->GetModelIndex( pModelName );
	m_solidIndex = solid.index;
	SetAbsOrigin( position );
	SetAbsAngles( orientation );
	SetMoveType( MOVETYPE_PUSH );
	SetSolid( SOLID_VPHYSICS );
	SetCollisionGroup( pOwner->GetCollisionGroup() );
	AddSolidFlags( FSOLID_CUSTOMRAYTEST | FSOLID_CUSTOMBOXTEST );
	solid.params.pGameData = (void *)this;
	IPhysicsObject *pPhysics = VPhysicsInitShadow( false, false, &solid );
	if ( !pPhysics )
		return false;

	// we can't use the default model bounds because each entity is only one bone of the model
	// so compute the OBB of the physics model and use that.
	Vector mins, maxs;
	physcollision->CollideGetAABB( &mins, &maxs, pPhysics->GetCollide(), vec3_origin, vec3_angle );
	SetCollisionBounds( mins, maxs );

	pPhysics->SetCallbackFlags( pPhysics->GetCallbackFlags() | CALLBACK_GLOBAL_TOUCH );
	pPhysics->EnableGravity( false );
	// This is not a normal shadow controller that is trying to go to a space occupied by an entity in the game physics
	// This entity is not running PhysicsPusher(), so Vphysics is supposed to move it
	// This line of code informs vphysics of that fact
	if ( pOwner->IsNPC() )
	{
		pPhysics->GetShadowController()->SetPhysicallyControlled( true );
	}

	return true;
}
//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
bool CTriggerPortalCleanser::TransferPhysicsObject( CBaseEntity *pFrom, CBaseEntity *pTo, bool wakeUp )
{
	IPhysicsObject *pVPhysics = pFrom->VPhysicsGetObject();
	if ( !pVPhysics || pVPhysics->IsStatic() )
		return false;

	Vector vecVelocity, vecAngular;
	pVPhysics->GetVelocity( &vecVelocity, &vecAngular );

	// clear out the pointer so it won't get deleted
	pFrom->VPhysicsSwapObject( NULL );
	// remove any AI behavior bound to it
	pVPhysics->RemoveShadowController();
	// transfer to the new owner
	pTo->VPhysicsSetObject( pVPhysics );
	pVPhysics->SetGameData( (void *)pTo );
	pTo->VPhysicsUpdate( pVPhysics );
	
	// may have been temporarily disabled by the old object
	pVPhysics->EnableMotion( true );
	pVPhysics->EnableGravity( false );
	
	// Slow down and push up the object.
	vecVelocity /= 2;
	vecAngular /= 2;
	vecVelocity.z += 10;
	pVPhysics->SetVelocity( &vecVelocity, &vecAngular );

	// Update for the new entity solid type
	pVPhysics->RecheckCollisionFilter();
	if ( wakeUp )
	{
		pVPhysics->Wake();
	}

	return true;
}
void CTFGrenadeNailProjectile::StartEmittingNails( void )
{
    // 0.4 seconds later, emit nails
    IPhysicsObject *pPhysicsObject = VPhysicsGetObject();

    if ( pPhysicsObject )
    {
        m_pMotionController = physenv->CreateMotionController( &m_GrenadeController );
        m_pMotionController->AttachObject( pPhysicsObject, true );

        pPhysicsObject->EnableGravity( false );

        pPhysicsObject->Wake();
    }

    QAngle ang(0,0,0);
    Vector pos = GetAbsOrigin();
    pos.z += 32;
    m_GrenadeController.SetDesiredPosAndOrientation( pos, ang );

    m_flNailAngle = 0;
    m_iNumNailBurstsLeft = 40;

    int animDesired = SelectWeightedSequence( ACT_RANGE_ATTACK1 );
    ResetSequence( animDesired );
    SetPlaybackRate( 1.0 );

    Vector soundPosition = GetAbsOrigin() + Vector( 0, 0, 5 );
    CPASAttenuationFilter filter( soundPosition );
    EmitSound( filter, entindex(), "Weapon_Grenade_Nail.Launch" );

#ifdef GAME_DLL
    SetThink( &CTFGrenadeNailProjectile::EmitNails );
    SetNextThink( gpGlobals->curtime + 0.4 );
#endif
}
Example #5
0
// UNDONE: Is this worth it?, just recreate the object instead? (that happens when this returns false anyway)
// recreating works, but is more expensive and won't inherit properties (velocity, constraints, etc)
bool TransferPhysicsObject( CBaseEntity *pFrom, CBaseEntity *pTo )
{
	IPhysicsObject *pVPhysics = pFrom->VPhysicsGetObject();
	if ( !pVPhysics || pVPhysics->IsStatic() )
		return false;

	// clear out the pointer so it won't get deleted
	pFrom->VPhysicsSwapObject( NULL );
	// remove any AI behavior bound to it
	pVPhysics->RemoveShadowController();
	// transfer to the new owner
	pTo->VPhysicsSetObject( pVPhysics );
	pVPhysics->SetGameData( (void *)pTo );
	pTo->VPhysicsUpdate( pVPhysics );
	
	// may have been temporarily disabled by the old object
	pVPhysics->EnableMotion( true );
	pVPhysics->EnableGravity( true );
	
	// Update for the new entity solid type
	pVPhysics->RecheckCollisionFilter();

	return true;
}
bool CStatueProp::CreateVPhysicsFromOBBs( CBaseAnimating *pInitBaseAnimating )
{
	// Make enough pointers to convexes for each hitbox
	CPhysConvex **ppConvex = new (CPhysConvex*[ m_pInitOBBs->Count() ]);

	float flTotalVolume = 0.0f;
	float flTotalSurfaceArea = 0.0f;

	for ( int i = 0; i < m_pInitOBBs->Count(); i++ )
	{
		const outer_collision_obb_t *pOBB = &((*m_pInitOBBs)[ i ]);

		// Accumulate volume and area
		Vector flDimentions = pOBB->vecMaxs - pOBB->vecMins;
		flTotalVolume += flDimentions.x * flDimentions.y * flDimentions.z;
		flTotalSurfaceArea += 2.0f * ( flDimentions.x * flDimentions.y + flDimentions.x * flDimentions.z + flDimentions.y * flDimentions.z );

		// Get angled min and max extents
		Vector vecMins, vecMaxs;
		VectorRotate( pOBB->vecMins, pOBB->angAngles, vecMins );
		VectorRotate( pOBB->vecMaxs, pOBB->angAngles, vecMaxs );

		// Get the corners in world space
		Vector vecMinCorner = pOBB->vecPos + vecMins;
		Vector vecMaxCorner = pOBB->vecPos + vecMaxs;

		// Get the normals of the hitbox in world space
		Vector vecForward, vecRight, vecUp;
		AngleVectors( pOBB->angAngles, &vecForward, &vecRight, &vecUp );
		vecRight = -vecRight;

		// Convert corners and normals to local space
		Vector vecCornerLocal[ 2 ];
		Vector vecNormalLocal[ 3 ];

		matrix3x4_t matToWorld = EntityToWorldTransform();
		VectorITransform( vecMaxCorner, matToWorld, vecCornerLocal[ 0 ] );
		VectorITransform( vecMinCorner, matToWorld, vecCornerLocal[ 1 ] );
		VectorIRotate( vecForward, matToWorld, vecNormalLocal[ 0 ] );
		VectorIRotate( vecRight, matToWorld, vecNormalLocal[ 1 ] );
		VectorIRotate( vecUp, matToWorld, vecNormalLocal[ 2 ] );

		// Create 6 planes from the local oriented hit box data
		float pPlanes[ 4 * 6 ];

		for ( int iPlane = 0; iPlane < 6; ++iPlane )
		{
			int iPlaneMod2 = iPlane % 2;
			int iPlaneDiv2 = iPlane / 2;
			bool bOdd = ( iPlaneMod2 == 1 );

			// Plane Normal
			pPlanes[ iPlane * 4 + 0 ] = vecNormalLocal[ iPlaneDiv2 ].x * ( bOdd ? -1.0f : 1.0f );
			pPlanes[ iPlane * 4 + 1 ] = vecNormalLocal[ iPlaneDiv2 ].y * ( bOdd ? -1.0f : 1.0f );
			pPlanes[ iPlane * 4 + 2 ] = vecNormalLocal[ iPlaneDiv2 ].z * ( bOdd ? -1.0f : 1.0f );

			// Plane D
			pPlanes[ iPlane * 4 + 3 ] = ( vecCornerLocal[ iPlaneMod2 ].x * vecNormalLocal[ iPlaneDiv2 ].x + 
				vecCornerLocal[ iPlaneMod2 ].y * vecNormalLocal[ iPlaneDiv2 ].y + 
				vecCornerLocal[ iPlaneMod2 ].z * vecNormalLocal[ iPlaneDiv2 ].z ) * ( bOdd ? -1.0f : 1.0f );
		}

		// Create convex from the intersection of these planes
		ppConvex[ i ] = physcollision->ConvexFromPlanes( pPlanes, 6, 0.0f );
	}

	// Make a single collide out of the group of convex boxes
	CPhysCollide *pPhysCollide = physcollision->ConvertConvexToCollide( ppConvex, m_pInitOBBs->Count() );

	delete[] ppConvex;

	// Create the physics object
	objectparams_t params = g_PhysDefaultObjectParams;
	params.pGameData = static_cast<void *>( this );

	int nMaterialIndex = physprops->GetSurfaceIndex( "ice" );	// use ice material

	IPhysicsObject* p = physenv->CreatePolyObject( pPhysCollide, nMaterialIndex, GetAbsOrigin(), GetAbsAngles(), &params );
	Assert( p != NULL );

	// Set velocity
	Vector vecInitialVelocity = pInitBaseAnimating->GetAbsVelocity();
	p->SetVelocity( &vecInitialVelocity, NULL );

	// Compute mass
	float flMass;
	float flDensity, flThickness;
	physprops->GetPhysicsProperties( nMaterialIndex, &flDensity, &flThickness, NULL, NULL );

	// Make it more hollow
	flThickness = MIN ( 1.0f, flThickness + 0.5f );

	if ( flThickness > 0.0f )
	{
		flMass = flTotalSurfaceArea * flThickness * CUBIC_METERS_PER_CUBIC_INCH * flDensity;
	}
	else
	{
		// density is in kg/m^3, volume is in in^3
		flMass = flTotalVolume * CUBIC_METERS_PER_CUBIC_INCH * flDensity;
	}

	// Mass is somewhere between the original and if it was all ice
	p->SetMass( flMass );

	// Yes, gravity
	p->EnableGravity( true );

	// Use this as our vphysics
	VPhysicsSetObject( p );

	SetSolid( SOLID_VPHYSICS );
	AddSolidFlags( FSOLID_CUSTOMRAYTEST | FSOLID_CUSTOMBOXTEST );

	SetMoveType( MOVETYPE_VPHYSICS );

	m_pInitOBBs = NULL;

	return true;
}
bool CStatueProp::CreateVPhysicsFromHitBoxes( CBaseAnimating *pInitBaseAnimating )
{
	if ( !pInitBaseAnimating )
		return false;

	// Use the current animation sequence and cycle
	CopyAnimationDataFrom( pInitBaseAnimating );

	// Copy over any render color
	color24 colorRender = pInitBaseAnimating->GetRenderColor();
	SetRenderColor( colorRender.r, colorRender.g, colorRender.b );
	SetRenderAlpha( pInitBaseAnimating->GetRenderAlpha() );

	// Get hitbox data
	CStudioHdr *pStudioHdr = GetModelPtr();
	if ( !pStudioHdr )
		return false;

	mstudiohitboxset_t *set = pStudioHdr->pHitboxSet( m_nHitboxSet );
	if ( !set )
		return false;

	Vector position;
	QAngle angles;

	// Make enough pointers to convexes for each hitbox
	CPhysConvex **ppConvex = new (CPhysConvex*[ set->numhitboxes ]);

	float flTotalVolume = 0.0f;
	float flTotalSurfaceArea = 0.0f;

	for ( int i = 0; i < set->numhitboxes; i++ )
	{
		// Get the hitbox info
		mstudiobbox_t *pbox = set->pHitbox( i );
		GetBonePosition( pbox->bone, position, angles );

		// Accumulate volume and area
		Vector flDimentions = pbox->bbmax - pbox->bbmin;
		flTotalVolume += flDimentions.x * flDimentions.y * flDimentions.z;
		flTotalSurfaceArea += 2.0f * ( flDimentions.x * flDimentions.y + flDimentions.x * flDimentions.z + flDimentions.y * flDimentions.z );

		// Get angled min and max extents
		Vector vecMins, vecMaxs;
		VectorRotate( pbox->bbmin, angles, vecMins );
		VectorRotate( pbox->bbmax, angles, vecMaxs );

		// Get the corners in world space
		Vector vecMinCorner = position + vecMins;
		Vector vecMaxCorner = position + vecMaxs;

		// Get the normals of the hitbox in world space
		Vector vecForward, vecRight, vecUp;
		AngleVectors( angles, &vecForward, &vecRight, &vecUp );
		vecRight = -vecRight;

		// Convert corners and normals to local space
		Vector vecCornerLocal[ 2 ];
		Vector vecNormalLocal[ 3 ];

		matrix3x4_t matToWorld = EntityToWorldTransform();
		VectorITransform( vecMaxCorner, matToWorld, vecCornerLocal[ 0 ] );
		VectorITransform( vecMinCorner, matToWorld, vecCornerLocal[ 1 ] );
		VectorIRotate( vecForward, matToWorld, vecNormalLocal[ 0 ] );
		VectorIRotate( vecRight, matToWorld, vecNormalLocal[ 1 ] );
		VectorIRotate( vecUp, matToWorld, vecNormalLocal[ 2 ] );

		// Create 6 planes from the local oriented hit box data
		float pPlanes[ 4 * 6 ];

		for ( int iPlane = 0; iPlane < 6; ++iPlane )
		{
			int iPlaneMod2 = iPlane % 2;
			int iPlaneDiv2 = iPlane / 2;
			bool bOdd = ( iPlaneMod2 == 1 );

			// Plane Normal
			pPlanes[ iPlane * 4 + 0 ] = vecNormalLocal[ iPlaneDiv2 ].x * ( bOdd ? -1.0f : 1.0f );
			pPlanes[ iPlane * 4 + 1 ] = vecNormalLocal[ iPlaneDiv2 ].y * ( bOdd ? -1.0f : 1.0f );
			pPlanes[ iPlane * 4 + 2 ] = vecNormalLocal[ iPlaneDiv2 ].z * ( bOdd ? -1.0f : 1.0f );

			// Plane D
			pPlanes[ iPlane * 4 + 3 ] = ( vecCornerLocal[ iPlaneMod2 ].x * vecNormalLocal[ iPlaneDiv2 ].x + 
				vecCornerLocal[ iPlaneMod2 ].y * vecNormalLocal[ iPlaneDiv2 ].y + 
				vecCornerLocal[ iPlaneMod2 ].z * vecNormalLocal[ iPlaneDiv2 ].z ) * ( bOdd ? -1.0f : 1.0f );
		}

		// Create convex from the intersection of these planes
		ppConvex[ i ] = physcollision->ConvexFromPlanes( pPlanes, 6, 0.0f );
	}

	// Make a single collide out of the group of convex boxes
	CPhysCollide *pPhysCollide = physcollision->ConvertConvexToCollide( ppConvex, set->numhitboxes );

	delete[] ppConvex;

	// Create the physics object
	objectparams_t params = g_PhysDefaultObjectParams;
	params.pGameData = static_cast<void *>( this );

	int nMaterialIndex = physprops->GetSurfaceIndex( "ice" );	// use ice material

	IPhysicsObject* p = physenv->CreatePolyObject( pPhysCollide, nMaterialIndex, GetAbsOrigin(), GetAbsAngles(), &params );
	Assert( p != NULL );

	// Set velocity
	Vector vecInitialVelocity = pInitBaseAnimating->GetAbsVelocity();
	p->SetVelocity( &vecInitialVelocity, NULL );

	// Compute mass
	float flMass;
	float flDensity, flThickness;
	physprops->GetPhysicsProperties( nMaterialIndex, &flDensity, &flThickness, NULL, NULL );

	// Make it more hollow
	flThickness = MIN ( 1.0f, flThickness + 0.5f );

	if ( flThickness > 0.0f )
	{
		flMass = flTotalSurfaceArea * flThickness * CUBIC_METERS_PER_CUBIC_INCH * flDensity;
	}
	else
	{
		// density is in kg/m^3, volume is in in^3
		flMass = flTotalVolume * CUBIC_METERS_PER_CUBIC_INCH * flDensity;
	}

	// Mass is somewhere between the original and if it was all ice
	p->SetMass( flMass );

	// Yes, gravity
	p->EnableGravity( true );

	// Use this as our vphysics
	VPhysicsSetObject( p );

	SetSolid( SOLID_VPHYSICS );
	AddSolidFlags( FSOLID_CUSTOMRAYTEST | FSOLID_CUSTOMBOXTEST );

	SetMoveType( MOVETYPE_VPHYSICS );

	if ( pInitBaseAnimating != this )
	{
		// Transfer children from the init base animating
		TransferChildren( pInitBaseAnimating, this );

		CBaseEntity *pChild = FirstMoveChild();

		while ( pChild )
		{
			CEntityFreezing *pFreezing = dynamic_cast<CEntityFreezing*>( pChild );
			if ( pFreezing )
			{
				pFreezing->FinishFreezing();
			}

			pChild = pChild->NextMovePeer();
		}
	}

	return true;
}
//-----------------------------------------------------------------------------
// Purpose: Called when an entity starts touching us.
// Input  : pOther - The entity that is touching us.
//-----------------------------------------------------------------------------
void CTriggerPortalCleanser::StartTouch( CBaseEntity *pOther )
{
	if ( PassesTriggerFilters(pOther) )
	{
		EHANDLE hOther;
		hOther = pOther;
		
		bool bAdded = false;
		if ( m_hTouchingEntities.Find( hOther ) == m_hTouchingEntities.InvalidIndex() )
		{
			m_hTouchingEntities.AddToTail( hOther );
			bAdded = true;
		}

		m_OnStartTouch.FireOutput(pOther, this);

		if ( bAdded && ( m_hTouchingEntities.Count() == 1 ) )
		{
			// First entity to touch us that passes our filters
			m_OnStartTouchAll.FireOutput( pOther, this );
		}

		if ( portal_cleanser_debug.GetBool() )
		{
			Msg("%s START-TOUCH: for %s\n", GetDebugName(), pOther->GetDebugName() );
		}

		if ( !pOther->IsPlayer() )
		{
			CBaseAnimating *pAnim = pOther->GetBaseAnimating();
			if ( !pAnim )
				return;

			// Can't dissolve twice.
			if ( pAnim->IsDissolving() )
				return;

			// Force player to drop this object.
			Pickup_ForcePlayerToDropThisObject( pOther );

			if ( !FClassnameIs( pOther, "prop_physics" ) )
			{
				if ( FClassnameIs( pOther, "simple_physics_prop" ) || FClassnameIs( pOther, "simple_physics_brush" ) )
				{
					// simple_physics_prop ?
					return;
				}

				if ( portal_cleanser_debug.GetBool() )
				{
					Msg("%s IS CREATING: simple_physics_prop\n", GetDebugName());
				}
				// Other object needs to be replaced by simple_physics_prop.
				pOther = CreateSimplePhysicsObject( pOther );

				// Dissolve the entity.
				CBaseAnimating *pAnim = pOther->GetBaseAnimating();
				pAnim->Dissolve( NULL, gpGlobals->curtime, false, ENTITY_DISSOLVE_NORMAL );

				if ( portal_cleanser_debug.GetBool() )
				{
					Msg("%s DISSOLVE SIMPLE PHYSICS: %s\n", GetDebugName(), pOther->GetDebugName() );
				}

				// Outputs
				m_hActivator = pOther;
				m_OnDissolve.FireOutput(m_hActivator, this);

				return;
			}

			IPhysicsObject *pPhysics = pOther->VPhysicsGetObject();
			if ( pPhysics )
			{
				// Dissolve the entity.
				pAnim->Dissolve( NULL, gpGlobals->curtime, false, ENTITY_DISSOLVE_NORMAL );

				if ( portal_cleanser_debug.GetBool() )
				{
					Msg("%s DISSOLVE PHYSICS: %s\n", GetDebugName(), pOther->GetDebugName() );
				}

				// Turn off the gravity for this object.
				pPhysics->EnableGravity( false );

				// Slow down and push up the object.
				Vector vecVelocity, vecAngular;
				pPhysics->GetVelocity( &vecVelocity, &vecAngular );
				vecVelocity /= 2;
				vecAngular /= 2;
				vecVelocity.z += 10;
				pPhysics->SetVelocity( &vecVelocity, &vecAngular );

				// Outputs
				m_hActivator = pOther;
				m_OnDissolve.FireOutput(m_hActivator, this);

				const char *pModelName = STRING( pOther->GetModelName() );
				if ( Q_strstr( pModelName, "models/props/metal_box.mdl" ) )
				{
					m_OnDissolveBox.FireOutput(m_hActivator, this);
				}
			}
		}
	}
}