void CGrabController::ComputeMaxSpeed( CBaseEntity *pEntity, IPhysicsObject *pPhysics ) { m_shadow.maxSpeed = 1000; m_shadow.maxAngular = DEFAULT_MAX_ANGULAR; // Compute total mass... float flMass = PhysGetEntityMass( pEntity ); float flMaxMass = playerpickup_maxmass.GetFloat(); if ( flMass <= flMaxMass ) return; float flLerpFactor = clamp( flMass, flMaxMass, 500.0f ); flLerpFactor = SimpleSplineRemapVal( flLerpFactor, flMaxMass, 500.0f, 0.0f, 1.0f ); float invMass = pPhysics->GetInvMass(); float invInertia = pPhysics->GetInvInertia().Length(); float invMaxMass = 1.0f / MAX_MASS; float ratio = invMaxMass / invMass; invMass = invMaxMass; invInertia *= ratio; float maxSpeed = invMass * MASS_SPEED_SCALE * 200; float maxAngular = invInertia * MASS_SPEED_SCALE * 360; m_shadow.maxSpeed = Lerp( flLerpFactor, m_shadow.maxSpeed, maxSpeed ); m_shadow.maxAngular = Lerp( flLerpFactor, m_shadow.maxAngular, maxAngular ); }
void CEnvHeadcrabCanister::TestForCollisionsAgainstWorld( const Vector &vecEndPosition ) { // Splash damage! // Iterate on all entities in the vicinity. float flDamageRadius = m_flDamageRadius; float flDamage = m_flDamage; CBaseEntity *pEntity; for ( CEntitySphereQuery sphere( vecEndPosition, flDamageRadius ); ( pEntity = sphere.GetCurrentEntity() ) != NULL; sphere.NextEntity() ) { if ( pEntity == this ) continue; if ( !pEntity->IsSolid() ) continue; // Get distance to object and use it as a scale value. Vector vecSegment; VectorSubtract( pEntity->GetAbsOrigin(), vecEndPosition, vecSegment ); float flDistance = VectorNormalize( vecSegment ); float flFactor = 1.0f / ( flDamageRadius * (INNER_RADIUS_FRACTION - 1) ); flFactor *= flFactor; float flScale = flDistance - flDamageRadius; flScale *= flScale * flFactor; if ( flScale > 1.0f ) { flScale = 1.0f; } // Check for a physics object and apply force! Vector vecForceDir = vecSegment; IPhysicsObject *pPhysObject = pEntity->VPhysicsGetObject(); if ( pPhysObject ) { // Send it flying!!! float flMass = PhysGetEntityMass( pEntity ); vecForceDir *= flMass * 750 * flScale; pPhysObject->ApplyForceCenter( vecForceDir ); } if ( pEntity->m_takedamage && ( m_flDamage != 0.0f ) ) { CTakeDamageInfo info( this, this, flDamage * flScale, DMG_BLAST ); CalculateExplosiveDamageForce( &info, vecSegment, pEntity->GetAbsOrigin() ); pEntity->TakeDamage( info ); } if ( pEntity->IsPlayer() && !(static_cast<CBasePlayer*>(pEntity)->IsInAVehicle()) ) { if (vecSegment.z < 0.1f) { vecSegment.z = 0.1f; VectorNormalize( vecSegment ); } float flAmount = SimpleSplineRemapVal( flScale, 0.0f, 1.0f, 250.0f, 1000.0f ); pEntity->ApplyAbsVelocityImpulse( vecSegment * flAmount ); } } }
//----------------------------------------------------------------------------- // Test for impact! //----------------------------------------------------------------------------- void CEnvHeadcrabCanister::TestForCollisionsAgainstEntities( const Vector &vecEndPosition ) { // Debugging!! // NDebugOverlay::Box( GetAbsOrigin(), m_vecMin * 0.5f, m_vecMax * 0.5f, 255, 255, 0, 0, 5 ); // NDebugOverlay::Box( vecEndPosition, m_vecMin, m_vecMax, 255, 0, 0, 0, 5 ); float flRadius = CollisionProp()->BoundingRadius(); Vector vecMins( -flRadius, -flRadius, -flRadius ); Vector vecMaxs( flRadius, flRadius, flRadius ); Ray_t ray; ray.Init( GetAbsOrigin(), vecEndPosition, vecMins, vecMaxs ); CCollideList collideList( &ray, this, MASK_SOLID ); enginetrace->EnumerateEntities( ray, false, &collideList ); float flDamage = m_flDamage; // Now get each entity and react accordinly! for( int iEntity = collideList.m_Entities.Count(); --iEntity >= 0; ) { CBaseEntity *pEntity = collideList.m_Entities[iEntity]; Vector vecForceDir = m_Shared.m_vecDirection; // Check for a physics object and apply force! IPhysicsObject *pPhysObject = pEntity->VPhysicsGetObject(); if ( pPhysObject ) { float flMass = PhysGetEntityMass( pEntity ); vecForceDir *= flMass * 750; pPhysObject->ApplyForceCenter( vecForceDir ); } if ( pEntity->m_takedamage && ( m_flDamage != 0.0f ) ) { CTakeDamageInfo info( this, this, flDamage, DMG_BLAST ); CalculateExplosiveDamageForce( &info, vecForceDir, pEntity->GetAbsOrigin() ); pEntity->TakeDamage( info ); } } }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- float CalculatePhysicsImpactDamage( int index, gamevcollisionevent_t *pEvent, const impactdamagetable_t &table, float energyScale, bool allowStaticDamage, int &damageType, bool bDamageFromHeldObjects ) { damageType = DMG_CRUSH; int otherIndex = !index; // UNDONE: Expose a flag for self-inflicted damage? Can't think of a valid case so far. if ( pEvent->pEntities[0] == pEvent->pEntities[1] ) return 0; if ( pEvent->pObjects[otherIndex]->GetGameFlags() & FVPHYSICS_NO_NPC_IMPACT_DMG ) { if( pEvent->pEntities[index]->IsNPC() || pEvent->pEntities[index]->IsPlayer() ) { return 0; } } // use implicit velocities on ragdolls since they may have high constraint velocities that aren't actually executed, just pushed through contacts if (( pEvent->pObjects[otherIndex]->GetGameFlags() & FVPHYSICS_PART_OF_RAGDOLL) && pEvent->pEntities[index]->IsPlayer() ) { pEvent->pObjects[otherIndex]->GetImplicitVelocity( &pEvent->preVelocity[otherIndex], &pEvent->preAngularVelocity[otherIndex] ); } // Dissolving impact damage results in death always. if ( ( pEvent->pObjects[otherIndex]->GetGameFlags() & FVPHYSICS_DMG_DISSOLVE ) && !pEvent->pEntities[index]->IsEFlagSet(EFL_NO_DISSOLVE) ) { damageType |= DMG_DISSOLVE; return 1000; } if ( energyScale <= 0.0f ) return 0; const int gameFlagsNoDamage = FVPHYSICS_CONSTRAINT_STATIC | FVPHYSICS_NO_IMPACT_DMG; // NOTE: Crushing damage is handled by stress calcs in vphysics update functions, this is ONLY impact damage // this is a non-moving object due to a constraint - no damage if ( pEvent->pObjects[otherIndex]->GetGameFlags() & gameFlagsNoDamage ) return 0; // If it doesn't take damage from held objects and the object is being held - no damage if ( !bDamageFromHeldObjects && ( pEvent->pObjects[otherIndex]->GetGameFlags() & FVPHYSICS_PLAYER_HELD ) ) { // If it doesn't take damage from held objects - no damage if ( !bDamageFromHeldObjects ) return 0; } if ( pEvent->pObjects[otherIndex]->GetGameFlags() & FVPHYSICS_MULTIOBJECT_ENTITY ) { // UNDONE: Add up mass here for car wheels and prop_ragdoll pieces? IPhysicsObject *pList[VPHYSICS_MAX_OBJECT_LIST_COUNT]; int count = pEvent->pEntities[otherIndex]->VPhysicsGetObjectList( pList, ARRAYSIZE(pList) ); for ( int i = 0; i < count; i++ ) { if ( pList[i]->GetGameFlags() & gameFlagsNoDamage ) return 0; } } if ( pEvent->pObjects[index]->GetGameFlags() & FVPHYSICS_PLAYER_HELD ) { // players can't damage held objects if ( pEvent->pEntities[otherIndex]->IsPlayer() ) return 0; allowStaticDamage = false; } #if 0 { PhysGetDamageInflictorVelocityStartOfFrame( pEvent->pObjects[otherIndex], pEvent->preVelocity[otherIndex], pEvent->preAngularVelocity[otherIndex] ); } #endif float otherSpeedSqr = pEvent->preVelocity[otherIndex].LengthSqr(); float otherAngSqr = 0; // factor in angular for sharp objects if ( pEvent->pObjects[otherIndex]->GetGameFlags() & FVPHYSICS_DMG_SLICE ) { otherAngSqr = pEvent->preAngularVelocity[otherIndex].LengthSqr(); } float otherMass = pEvent->pObjects[otherIndex]->GetMass(); if ( pEvent->pObjects[otherIndex]->GetGameFlags() & FVPHYSICS_PLAYER_HELD ) { // if the player is holding the object, use its real mass (player holding reduced the mass) CBasePlayer *pPlayer = NULL; if ( gpGlobals->maxClients == 1 ) { pPlayer = UTIL_GetLocalPlayer(); } else { // See which MP player is holding the physics object and then use that player to get the real mass of the object. // This is ugly but better than having linkage between an object and its "holding" player. for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { CBasePlayer *tempPlayer = UTIL_PlayerByIndex( i ); if ( tempPlayer && pEvent->pEntities[index] == tempPlayer->GetHeldObject() ) { pPlayer = tempPlayer; break; } } } if ( pPlayer ) { otherMass = pPlayer->GetHeldObjectMass( pEvent->pObjects[otherIndex] ); } } // NOTE: sum the mass of each object in this system for the purpose of damage if ( pEvent->pEntities[otherIndex] && (pEvent->pObjects[otherIndex]->GetGameFlags() & FVPHYSICS_MULTIOBJECT_ENTITY) ) { otherMass = PhysGetEntityMass( pEvent->pEntities[otherIndex] ); } if ( pEvent->pObjects[otherIndex]->GetGameFlags() & FVPHYSICS_HEAVY_OBJECT ) { otherMass = table.largeMassMin; if ( energyScale < 2.0f ) { energyScale = 2.0f; } } // UNDONE: allowStaticDamage is a hack - work out some method for // breakable props to impact the world and break!! if ( !allowStaticDamage ) { if ( otherMass < table.minMass ) return 0; // check to see if the object is small if ( otherMass < table.smallMassMax && otherSpeedSqr < table.smallMassMinSpeedSqr ) return 0; if ( otherSpeedSqr < table.minSpeedSqr && otherAngSqr < table.minRotSpeedSqr ) return 0; } // Add extra oomph for floating objects if ( pEvent->pEntities[index]->IsFloating() && !pEvent->pEntities[otherIndex]->IsWorld() ) { if ( energyScale < 3.0f ) { energyScale = 3.0f; } } float damage = 0; bool bDebug = false;//(&table == &gDefaultPlayerImpactDamageTable); // don't ever take spin damage from slowly spinning objects if ( otherAngSqr > table.minRotSpeedSqr ) { Vector otherInertia = pEvent->pObjects[otherIndex]->GetInertia(); float angularMom = DotProductAbs( otherInertia, pEvent->preAngularVelocity[otherIndex] ); damage = ReadDamageTable( table.angularTable, table.angularCount, angularMom * energyScale, bDebug ); if ( damage > 0 ) { // Msg("Spin : %.1f, Damage %.0f\n", FastSqrt(angularMom), damage ); damageType |= DMG_SLASH; } } float deltaV = pEvent->preVelocity[index].Length() - pEvent->postVelocity[index].Length(); float mass = pEvent->pObjects[index]->GetMass(); // If I lost speed, and I lost less than min velocity, then filter out this energy if ( deltaV > 0 && deltaV < table.myMinVelocity ) { deltaV = 0; } float eliminatedEnergy = deltaV * deltaV * mass; deltaV = pEvent->preVelocity[otherIndex].Length() - pEvent->postVelocity[otherIndex].Length(); float otherEliminatedEnergy = deltaV * deltaV * otherMass; // exaggerate the effects of really large objects if ( otherMass >= table.largeMassMin ) { otherEliminatedEnergy *= table.largeMassScale; float dz = pEvent->preVelocity[otherIndex].z - pEvent->postVelocity[otherIndex].z; if ( deltaV > 0 && dz < 0 && pEvent->preVelocity[otherIndex].z < 0 ) { float factor = fabs(dz / deltaV); otherEliminatedEnergy *= (1 + factor * (table.largeMassFallingScale - 1.0f)); } } eliminatedEnergy += otherEliminatedEnergy; // now in units of this character's speed squared float invMass = pEvent->pObjects[index]->GetInvMass(); if ( !pEvent->pObjects[index]->IsMoveable() ) { // inv mass is zero, but impact damage is enabled on this // prop, so recompute: invMass = 1.0f / pEvent->pObjects[index]->GetMass(); } else if ( pEvent->pObjects[index]->GetGameFlags() & FVPHYSICS_PLAYER_HELD ) { // if the player is holding the object, use it's real mass (player holding reduced the mass) CBasePlayer *pPlayer = NULL; if ( gpGlobals->maxClients == 1 ) { pPlayer = UTIL_GetLocalPlayer(); } else { // See which MP player is holding the physics object and then use that player to get the real mass of the object. // This is ugly but better than having linkage between an object and its "holding" player. for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { CBasePlayer *tempPlayer = UTIL_PlayerByIndex( i ); if ( tempPlayer && pEvent->pEntities[index] == tempPlayer->GetHeldObject() ) { pPlayer = tempPlayer; break; } } } if ( pPlayer ) { float mass = pPlayer->GetHeldObjectMass( pEvent->pObjects[index] ); if ( mass > 0 ) { invMass = 1.0f / mass; } } } eliminatedEnergy *= invMass * energyScale; damage += ReadDamageTable( table.linearTable, table.linearCount, eliminatedEnergy, bDebug ); if ( !pEvent->pObjects[otherIndex]->IsStatic() && otherMass < table.smallMassMax && table.smallMassCap > 0 ) { damage = clamp( damage, 0.f, table.smallMassCap ); } return damage; }