static CBaseEntity *FindPhysicsBlocker( IPhysicsObject *pPhysics ) { IPhysicsFrictionSnapshot *pSnapshot = pPhysics->CreateFrictionSnapshot(); CBaseEntity *pBlocker = NULL; float maxVel = 10.0f; while ( pSnapshot->IsValid() ) { IPhysicsObject *pOther = pSnapshot->GetObject(1); if ( pOther->IsMoveable() ) { CBaseEntity *pOtherEntity = static_cast<CBaseEntity *>(pOther->GetGameData()); // dot with this if you have a direction //Vector normal; //pSnapshot->GetSurfaceNormal(normal); float force = pSnapshot->GetNormalForce(); float vel = force * pOther->GetInvMass(); if ( vel > maxVel ) { pBlocker = pOtherEntity; maxVel = vel; } } pSnapshot->NextFrictionData(); } pPhysics->DestroyFrictionSnapshot( pSnapshot ); return pBlocker; }
void CStatueProp::Event_Killed( const CTakeDamageInfo &info ) { IPhysicsObject *pPhysics = VPhysicsGetObject(); if ( pPhysics && !pPhysics->IsMoveable() ) { pPhysics->EnableMotion( true ); VPhysicsTakeDamage( info ); } m_nShatterFlags = 0; // If you have some flags to network for the shatter effect, put them here! m_vShatterPosition = info.GetDamagePosition(); m_vShatterForce = info.GetDamageForce(); m_bShatter = true; // Skip over breaking code! //Break( info.GetInflictor(), info ); //BaseClass::Event_Killed( info ); // FIXME: Short delay before we actually remove so that the client statue gets a network update before we need it // This isn't a reliable way to do this and needs to be rethought. AddSolidFlags( FSOLID_NOT_SOLID ); SetNextThink( gpGlobals->curtime + 0.2f ); SetThink( &CBaseEntity::SUB_Remove ); }
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 ); } } }
void CASW_Barrel_Explosive::Event_Killed( const CTakeDamageInfo &info ) { IPhysicsObject *pPhysics = VPhysicsGetObject(); if ( pPhysics && !pPhysics->IsMoveable() ) { pPhysics->EnableMotion( true ); VPhysicsTakeDamage( info ); } QueueForExplode( info ); // Break( info.GetInflictor(), info ); // DoExplosion(); }
void CHL1_Player::Touch( CBaseEntity *pOther ) { if ( pOther == GetGroundEntity() ) return; if ( pOther->GetMoveType() != MOVETYPE_VPHYSICS || pOther->GetSolid() != SOLID_VPHYSICS ) return; IPhysicsObject *pPhys = pOther->VPhysicsGetObject(); if ( !pPhys || !pPhys->IsMoveable() ) return; SetTouchedPhysics( true ); }
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; }
//----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // adnan // want to add an angles modifier key bool CGravControllerPoint::UpdateObject( CBasePlayer *pPlayer, CBaseEntity *pEntity ) { if ( !pEntity || pPlayer->GetGroundEntity() == pEntity || !pEntity->VPhysicsGetObject() ) { return false; } //Adrian: Oops, our object became motion disabled, let go! IPhysicsObject *pPhys = pEntity->VPhysicsGetObject(); if ( pPhys && pPhys->IsMoveable() == false ) { return false; } SetTargetPosition( m_targetPosition, m_targetRotation ); return true; }
void CGEPropDynamic::Event_Killed(const CTakeDamageInfo &info) { // More or less just the kill event copied straight from prop_physics_respawnable, though it does not teleport as it cannot move on its own. IPhysicsObject *pPhysics = VPhysicsGetObject(); if (pPhysics && !pPhysics->IsMoveable()) { pPhysics->EnableMotion(true); VPhysicsTakeDamage(info); } Break(info.GetInflictor(), info); PhysCleanupFrictionSounds(this); VPhysicsDestroyObject(); CBaseEntity::PhysicsRemoveTouchedList(this); CBaseEntity::PhysicsRemoveGroundList(this); DestroyAllDataObjects(); AddEffects(EF_NODRAW); if (IsOnFire() || IsDissolving()) { UTIL_Remove(GetEffectEntity()); } SetContextThink(NULL, 0, "PROP_CLEARFLAGS"); if (m_flRespawnTime > 0) { SetThink(&CGEPropDynamic::Materialize); SetNextThink(gpGlobals->curtime + m_flRespawnTime); } }
//----------------------------------------------------------------------------- //----------------------------------------------------------------------------- bool CGrabController::UpdateObject( CBasePlayer *pPlayer, float flError ) { CBaseEntity *pEntity = GetAttached(); if ( !pEntity || ComputeError() > flError || pPlayer->GetGroundEntity() == pEntity || !pEntity->VPhysicsGetObject() ) { return false; } //Adrian: Oops, our object became motion disabled, let go! IPhysicsObject *pPhys = pEntity->VPhysicsGetObject(); if ( pPhys && pPhys->IsMoveable() == false ) { return false; } Vector forward, right, up; QAngle playerAngles = pPlayer->EyeAngles(); AngleVectors( playerAngles, &forward, &right, &up ); float pitch = AngleDistance(playerAngles.x,0); if( !m_bAllowObjectOverhead ) { playerAngles.x = clamp( pitch, -75, 75 ); } else { playerAngles.x = clamp( pitch, -90, 75 ); } // Now clamp a sphere of object radius at end to the player's bbox Vector radial = physcollision->CollideGetExtent( pPhys->GetCollide(), vec3_origin, pEntity->GetAbsAngles(), -forward ); Vector player2d = pPlayer->CollisionProp()->OBBMaxs(); float playerRadius = player2d.Length2D(); float radius = playerRadius + fabs(DotProduct( forward, radial )); float distance = 24 + ( radius * 2.0f ); // Add the prop's distance offset distance += m_flDistanceOffset; Vector start = pPlayer->Weapon_ShootPosition(); Vector end = start + ( forward * distance ); trace_t tr; CTraceFilterSkipTwoEntities traceFilter( pPlayer, pEntity, COLLISION_GROUP_NONE ); Ray_t ray; ray.Init( start, end ); enginetrace->TraceRay( ray, MASK_SOLID_BRUSHONLY, &traceFilter, &tr ); if ( tr.fraction < 0.5 ) { end = start + forward * (radius*0.5f); } else if ( tr.fraction <= 1.0f ) { end = start + forward * ( distance - radius ); } Vector playerMins, playerMaxs, nearest; pPlayer->CollisionProp()->WorldSpaceAABB( &playerMins, &playerMaxs ); Vector playerLine = pPlayer->CollisionProp()->WorldSpaceCenter(); CalcClosestPointOnLine( end, playerLine+Vector(0,0,playerMins.z), playerLine+Vector(0,0,playerMaxs.z), nearest, NULL ); if( !m_bAllowObjectOverhead ) { Vector delta = end - nearest; float len = VectorNormalize(delta); if ( len < radius ) { end = nearest + radius * delta; } } //Show overlays of radius if ( g_debug_physcannon.GetBool() ) { NDebugOverlay::Box( end, -Vector( 2,2,2 ), Vector(2,2,2), 0, 255, 0, true, 0 ); NDebugOverlay::Box( GetAttached()->WorldSpaceCenter(), -Vector( radius, radius, radius), Vector( radius, radius, radius ), 255, 0, 0, true, 0.0f ); } QAngle angles = TransformAnglesFromPlayerSpace( m_attachedAnglesPlayerSpace, pPlayer ); // If it has a preferred orientation, update to ensure we're still oriented correctly. Pickup_GetPreferredCarryAngles( pEntity, pPlayer, pPlayer->EntityToWorldTransform(), angles ); // We may be holding a prop that has preferred carry angles if ( m_bHasPreferredCarryAngles ) { matrix3x4_t tmp; ComputePlayerMatrix( pPlayer, tmp ); angles = TransformAnglesToWorldSpace( m_vecPreferredCarryAngles, tmp ); } matrix3x4_t attachedToWorld; Vector offset; AngleMatrix( angles, attachedToWorld ); VectorRotate( m_attachedPositionObjectSpace, attachedToWorld, offset ); SetTargetPosition( end - offset, angles ); return true; }
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; }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CASWEnvShake::ApplyShake( ShakeCommand_t command ) { if ( !HasSpawnFlags( SF_ASW_SHAKE_NO_VIEW ) ) { bool air = (GetSpawnFlags() & SF_ASW_SHAKE_INAIR) ? true : false; UTIL_ASW_ScreenShake( GetAbsOrigin(), Amplitude(), Frequency(), Duration(), Radius(), command, air ); } if ( GetSpawnFlags() & SF_ASW_SHAKE_ROPES ) { CRopeKeyframe::ShakeRopes( GetAbsOrigin(), Radius(), Frequency() ); } if ( GetSpawnFlags() & SF_ASW_SHAKE_PHYSICS ) { if ( !m_pShakeController ) { m_pShakeController = physenv->CreateMotionController( &m_shakeCallback ); } // do physics shake switch( command ) { case SHAKE_START: { m_stopTime = gpGlobals->curtime + Duration(); m_nextShake = 0; m_pShakeController->ClearObjects(); SetNextThink( gpGlobals->curtime ); m_currentAmp = Amplitude(); CBaseEntity *list[1024]; float radius = Radius(); // probably checked "Shake Everywhere" do a big radius if ( !radius ) { radius = MAX_COORD_INTEGER; } Vector extents = Vector(radius, radius, radius); Vector mins = GetAbsOrigin() - extents; Vector maxs = GetAbsOrigin() + extents; int count = UTIL_EntitiesInBox( list, 1024, mins, maxs, 0 ); for ( int i = 0; i < count; i++ ) { // // Only shake physics entities that players can see. This is one frame out of date // so it's possible that we could miss objects if a player changed PVS this frame. // if ( ( list[i]->GetMoveType() == MOVETYPE_VPHYSICS ) ) { IPhysicsObject *pPhys = list[i]->VPhysicsGetObject(); if ( pPhys && pPhys->IsMoveable() ) { m_pShakeController->AttachObject( pPhys, false ); pPhys->Wake(); } } } } break; case SHAKE_STOP: m_pShakeController->ClearObjects(); break; case SHAKE_AMPLITUDE: m_currentAmp = Amplitude(); case SHAKE_FREQUENCY: m_pShakeController->WakeObjects(); break; } } }
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; } }
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; } }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CPhysMagnet::Touch( CBaseEntity *pOther ) { // Ignore triggers if ( pOther->IsSolidFlagSet( FSOLID_NOT_SOLID ) ) return; m_bHasHitSomething = true; // Don't pickup if we're not active if ( !m_bActive ) return; // Hit our maximum? if ( m_iMaxObjectsAttached && m_iMaxObjectsAttached <= GetNumAttachedObjects() ) return; // Make sure it's made of metal trace_t tr = GetTouchTrace(); char cTexType = TEXTURETYPE_Find( &tr ); if ( cTexType != CHAR_TEX_METAL && cTexType != CHAR_TEX_COMPUTER ) { // See if the model is set to be metal if ( Q_strncmp( Studio_GetDefaultSurfaceProps( GetModelPtr() ), "metal", 5 ) ) return; } IPhysicsObject *pPhysics = pOther->VPhysicsGetObject(); if ( pPhysics && pOther->GetMoveType() == MOVETYPE_VPHYSICS && pPhysics->IsMoveable() ) { // Make sure we haven't already got this sucker on the magnet int iCount = m_MagnettedEntities.Count(); for ( int i = 0; i < iCount; i++ ) { if ( m_MagnettedEntities[i].hEntity == pOther ) return; } // We want to cast a long way to ensure our shadow shows up pOther->SetShadowCastDistance( 2048 ); // Create a constraint between the magnet and this sucker IPhysicsObject *pMagnetPhysObject = VPhysicsGetObject(); Assert( pMagnetPhysObject ); magnetted_objects_t newEntityOnMagnet; newEntityOnMagnet.hEntity = pOther; // Use the right constraint if ( HasSpawnFlags( SF_MAGNET_ALLOWROTATION ) ) { constraint_ballsocketparams_t ballsocket; ballsocket.Defaults(); ballsocket.constraint.Defaults(); ballsocket.constraint.forceLimit = lbs2kg(m_forceLimit); ballsocket.constraint.torqueLimit = lbs2kg(m_torqueLimit); pMagnetPhysObject->WorldToLocal( ballsocket.constraintPosition[0], tr.endpos ); pPhysics->WorldToLocal( ballsocket.constraintPosition[1], tr.endpos ); //newEntityOnMagnet.pConstraint = physenv->CreateBallsocketConstraint( pMagnetPhysObject, pPhysics, m_pConstraintGroup, ballsocket ); newEntityOnMagnet.pConstraint = physenv->CreateBallsocketConstraint( pMagnetPhysObject, pPhysics, NULL, ballsocket ); } else { constraint_fixedparams_t fixed; fixed.Defaults(); fixed.InitWithCurrentObjectState( pMagnetPhysObject, pPhysics ); fixed.constraint.Defaults(); fixed.constraint.forceLimit = lbs2kg(m_forceLimit); fixed.constraint.torqueLimit = lbs2kg(m_torqueLimit); // FIXME: Use the magnet's constraint group. //newEntityOnMagnet.pConstraint = physenv->CreateFixedConstraint( pMagnetPhysObject, pPhysics, m_pConstraintGroup, fixed ); newEntityOnMagnet.pConstraint = physenv->CreateFixedConstraint( pMagnetPhysObject, pPhysics, NULL, fixed ); } newEntityOnMagnet.pConstraint->SetGameData( (void *) this ); m_MagnettedEntities.AddToTail( newEntityOnMagnet ); m_flTotalMass += pPhysics->GetMass(); } DoMagnetSuck( pOther ); m_OnMagnetAttach.FireOutput( this, this ); }