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; }
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; }