//----------------------------------------------------------------------------- // Sets the collision bounds + the size //----------------------------------------------------------------------------- void CCollisionProperty::SetCollisionBounds( const Vector &mins, const Vector &maxs ) { if ( ( m_vecMinsPreScaled != mins ) || ( m_vecMaxsPreScaled != maxs ) ) { m_vecMinsPreScaled = mins; m_vecMaxsPreScaled = maxs; } bool bDirty = false; // Check if it's a scaled model CBaseAnimating *pAnim = GetOuter()->GetBaseAnimating(); if ( pAnim && pAnim->GetModelScale() != 1.0f ) { // Do the scaling Vector vecNewMins = mins * pAnim->GetModelScale(); Vector vecNewMaxs = maxs * pAnim->GetModelScale(); if ( ( m_vecMins != vecNewMins ) || ( m_vecMaxs != vecNewMaxs ) ) { m_vecMins = vecNewMins; m_vecMaxs = vecNewMaxs; bDirty = true; } } else { // No scaling needed! if ( ( m_vecMins != mins ) || ( m_vecMaxs != maxs ) ) { m_vecMins = mins; m_vecMaxs = maxs; bDirty = true; } } if ( bDirty ) { //ASSERT_COORD( m_vecMins.Get() ); //ASSERT_COORD( m_vecMaxs.Get() ); Vector vecSize; VectorSubtract( m_vecMaxs, m_vecMins, vecSize ); m_flRadius = vecSize.Length() * 0.5f; MarkSurroundingBoundsDirty(); } }
//----------------------------------------------------------------------------- // Sets the method by which the surrounding collision bounds is set //----------------------------------------------------------------------------- void CCollisionProperty::SetSurroundingBoundsType( SurroundingBoundsType_t type, const Vector *pMins, const Vector *pMaxs ) { m_nSurroundType = type; if (type != USE_SPECIFIED_BOUNDS) { Assert( !pMins && !pMaxs ); MarkSurroundingBoundsDirty(); } else { Assert( pMins && pMaxs ); m_vecSpecifiedSurroundingMinsPreScaled = *pMins; m_vecSpecifiedSurroundingMaxsPreScaled = *pMaxs; // Check if it's a scaled model CBaseAnimating *pAnim = GetOuter()->GetBaseAnimating(); if ( pAnim && pAnim->GetModelScale() != 1.0f ) { // Do the scaling Vector vecNewMins = *pMins * pAnim->GetModelScale(); Vector vecNewMaxs = *pMaxs * pAnim->GetModelScale(); m_vecSpecifiedSurroundingMins = vecNewMins; m_vecSpecifiedSurroundingMaxs = vecNewMaxs; m_vecSurroundingMins = vecNewMins; m_vecSurroundingMaxs = vecNewMaxs; } else { // No scaling needed! m_vecSpecifiedSurroundingMins = *pMins; m_vecSpecifiedSurroundingMaxs = *pMaxs; m_vecSurroundingMins = *pMins; m_vecSurroundingMaxs = *pMaxs; } ASSERT_COORD( m_vecSurroundingMins ); ASSERT_COORD( m_vecSurroundingMaxs ); } }
void PropBreakableCreateAll( int modelindex, IPhysicsObject *pPhysics, const breakablepropparams_t ¶ms, CBaseEntity *pEntity, int iPrecomputedBreakableCount, bool bIgnoreGibLimit, bool defaultLocation ) { // Check for prop breakable count reset. int nPropCount = props_break_max_pieces_perframe.GetInt(); if ( nPropCount != -1 ) { if ( nFrameNumber != gpGlobals->framecount ) { nPropBreakablesPerFrameCount = 0; nFrameNumber = gpGlobals->framecount; } // Check for max breakable count for the frame. if ( nPropBreakablesPerFrameCount >= nPropCount ) return; } int iMaxBreakCount = bIgnoreGibLimit ? -1 : props_break_max_pieces.GetInt(); if ( iMaxBreakCount != -1 ) { if ( iPrecomputedBreakableCount != -1 ) { iPrecomputedBreakableCount = MIN( iMaxBreakCount, iPrecomputedBreakableCount ); } else { iPrecomputedBreakableCount = iMaxBreakCount; } } #ifdef GAME_DLL // On server limit break model creation if ( !PropBreakableCapEdictsOnCreateAll(modelindex, pPhysics, params, pEntity, iPrecomputedBreakableCount ) ) { DevMsg( "Failed to create PropBreakable: would exceed MAX_EDICTS\n" ); return; } #endif vcollide_t *pCollide = modelinfo->GetVCollide( modelindex ); if ( !pCollide ) return; int nSkin = 0; CBaseEntity *pOwnerEntity = pEntity; CBaseAnimating *pOwnerAnim = NULL; if ( pPhysics ) { pOwnerEntity = static_cast<CBaseEntity *>(pPhysics->GetGameData()); } if ( pOwnerEntity ) { pOwnerAnim = pOwnerEntity->GetBaseAnimating(); if ( pOwnerAnim ) { nSkin = pOwnerAnim->m_nSkin; } } matrix3x4_t localToWorld; CStudioHdr studioHdr; const model_t *model = modelinfo->GetModel( modelindex ); if ( model ) { studioHdr.Init( modelinfo->GetStudiomodel( model ) ); } Vector parentOrigin = vec3_origin; int parentAttachment = Studio_FindAttachment( &studioHdr, "placementOrigin" ) + 1; if ( parentAttachment > 0 ) { GetAttachmentLocalSpace( &studioHdr, parentAttachment-1, localToWorld ); MatrixGetColumn( localToWorld, 3, parentOrigin ); } else { AngleMatrix( vec3_angle, localToWorld ); } CUtlVector<breakmodel_t> list; BreakModelList( list, modelindex, params.defBurstScale, params.defCollisionGroup ); CUtlVector< CBaseEntity* > spawnedGibs; if ( list.Count() ) { for ( int i = 0; i < list.Count(); i++ ) { int modelIndex = modelinfo->GetModelIndex( list[i].modelName ); if ( modelIndex <= 0 ) continue; // Skip multiplayer pieces that should be spawning on the other dll #ifdef GAME_DLL if ( gpGlobals->maxClients > 1 && breakable_multiplayer.GetBool() ) #else if ( gpGlobals->maxClients > 1 ) #endif { #ifdef GAME_DLL if ( list[i].mpBreakMode == MULTIPLAYER_BREAK_CLIENTSIDE ) continue; #else if ( list[i].mpBreakMode == MULTIPLAYER_BREAK_SERVERSIDE ) continue; #endif if ( !defaultLocation && list[i].mpBreakMode == MULTIPLAYER_BREAK_DEFAULT ) continue; } if ( ( nPropCount != -1 ) && ( nPropBreakablesPerFrameCount > nPropCount ) ) break; if ( ( iPrecomputedBreakableCount != -1 ) && ( i >= iPrecomputedBreakableCount ) ) break; matrix3x4_t matrix; AngleMatrix( params.angles, params.origin, matrix ); CStudioHdr studioHdr; const model_t *model = modelinfo->GetModel( modelIndex ); if ( model ) { studioHdr.Init( modelinfo->GetStudiomodel( model ) ); } // Increment the number of breakable props this frame. ++nPropBreakablesPerFrameCount; const float flModelScale = pOwnerAnim->GetModelScale(); Vector position = vec3_origin; QAngle angles = params.angles; if ( pOwnerAnim && list[i].placementName[0] ) { if ( list[i].placementIsBone ) { int boneIndex = pOwnerAnim->LookupBone( list[i].placementName ); if ( boneIndex >= 0 ) { pOwnerAnim->GetBonePosition( boneIndex, position, angles ); AngleMatrix( angles, position, matrix ); } } else { int attachmentIndex = Studio_FindAttachment( &studioHdr, list[i].placementName ) + 1; if ( attachmentIndex > 0 ) { pOwnerAnim->GetAttachment( attachmentIndex, matrix ); MatrixAngles( matrix, angles ); } } } else { int placementIndex = Studio_FindAttachment( &studioHdr, "placementOrigin" ) + 1; Vector placementOrigin = parentOrigin; if ( placementIndex > 0 ) { GetAttachmentLocalSpace( &studioHdr, placementIndex-1, localToWorld ); MatrixGetColumn( localToWorld, 3, placementOrigin ); placementOrigin -= parentOrigin; } VectorTransform( list[i].offset - placementOrigin * flModelScale, matrix, position ); } Vector objectVelocity = params.velocity; if (pPhysics) { pPhysics->GetVelocityAtPoint( position, &objectVelocity ); } int nActualSkin = nSkin; if ( nActualSkin > studioHdr.numskinfamilies() ) nActualSkin = 0; CBaseEntity *pBreakable = NULL; #ifdef GAME_DLL if ( GetGibManager() == NULL || GetGibManager()->AllowedToSpawnGib() ) #endif { pBreakable = BreakModelCreateSingle( pOwnerEntity, &list[i], position, angles, objectVelocity, params.angularVelocity, nActualSkin, params ); } if ( pBreakable ) { spawnedGibs.AddToTail( pBreakable ); #ifdef GAME_DLL if ( GetGibManager() ) { GetGibManager()->AddGibToLRU( pBreakable->GetBaseAnimating() ); } #endif if ( pOwnerEntity && pOwnerEntity->IsEffectActive( EF_NOSHADOW ) ) { pBreakable->AddEffects( EF_NOSHADOW ); } // If burst scale is set, this piece should 'burst' away from // the origin in addition to travelling in the wished velocity. if ( list[i].burstScale != 0.0 ) { Vector vecBurstDir = position - params.origin; // If $autocenter wasn't used, try the center of the piece if ( vecBurstDir == vec3_origin ) { vecBurstDir = pBreakable->WorldSpaceCenter() - params.origin; } VectorNormalize( vecBurstDir ); pBreakable->ApplyAbsVelocityImpulse( vecBurstDir * list[i].burstScale * flModelScale * params.velocityScale * params.burstScale ); } if ( params.randomAngularVelocity > 0.0f ) { AngularImpulse angRandomImpulse = RandomAngularImpulse( -params.randomAngularVelocity, params.randomAngularVelocity ); pBreakable->ApplyLocalAngularVelocityImpulse( angRandomImpulse * params.velocityScale ); } // If this piece is supposed to be motion disabled, disable it if ( list[i].isMotionDisabled ) { IPhysicsObject *pPhysicsObject = pBreakable->VPhysicsGetObject(); if ( pPhysicsObject != NULL ) { pPhysicsObject->EnableMotion( false ); } } } } } // Then see if the propdata specifies any breakable pieces else if ( pEntity ) { IBreakableWithPropData *pBreakableInterface = dynamic_cast<IBreakableWithPropData*>(pEntity); if ( pBreakableInterface && pBreakableInterface->GetBreakableModel() != NULL_STRING && pBreakableInterface->GetBreakableCount() ) { breakmodel_t breakModel; for ( int i = 0; i < pBreakableInterface->GetBreakableCount(); i++ ) { if ( ( iPrecomputedBreakableCount != -1 ) && ( i >= iPrecomputedBreakableCount ) ) break; Q_strncpy( breakModel.modelName, g_PropDataSystem.GetRandomChunkModel(STRING(pBreakableInterface->GetBreakableModel()), pBreakableInterface->GetMaxBreakableSize()), sizeof(breakModel.modelName) ); breakModel.health = 1; breakModel.fadeTime = RandomFloat(5,10); breakModel.fadeMinDist = 0.0f; breakModel.fadeMaxDist = 0.0f; breakModel.burstScale = params.defBurstScale; breakModel.collisionGroup = COLLISION_GROUP_DEBRIS; breakModel.isRagdoll = false; breakModel.isMotionDisabled = false; breakModel.placementName[0] = 0; breakModel.placementIsBone = false; Vector vecObbSize = pEntity->CollisionProp()->OBBSize(); // Find a random point on the plane of the original's two largest axis int smallestAxis = SmallestAxis( vecObbSize ); Vector vecMins(0,0,0); Vector vecMaxs(1,1,1); vecMins[smallestAxis] = 0.5; vecMaxs[smallestAxis] = 0.5; pEntity->CollisionProp()->RandomPointInBounds( vecMins, vecMaxs, &breakModel.offset ); // Push all chunks away from the center Vector vecBurstDir = breakModel.offset - params.origin; VectorNormalize( vecBurstDir ); Vector vecVelocity = vecBurstDir * params.defBurstScale; QAngle vecAngles = pEntity->GetAbsAngles(); int iSkin = pBreakableInterface->GetBreakableSkin(); CBaseEntity *pBreakable = NULL; #ifdef GAME_DLL if ( GetGibManager() == NULL || GetGibManager()->AllowedToSpawnGib() ) #endif { pBreakable = BreakModelCreateSingle( pOwnerEntity, &breakModel, breakModel.offset, vecAngles, vecVelocity, vec3_origin/*params.angularVelocity*/, iSkin, params ); if ( !pBreakable ) { DevWarning( "PropBreakableCreateAll: Could not create model %s\n", breakModel.modelName ); } } if ( pBreakable ) { #ifdef GAME_DLL if ( GetGibManager() ) { GetGibManager()->AddGibToLRU( pBreakable->GetBaseAnimating() ); } #endif Vector vecBreakableObbSize = pBreakable->CollisionProp()->OBBSize(); // Try to align the gibs along the original axis matrix3x4_t matrix; AngleMatrix( vecAngles, matrix ); AlignBoxes( &matrix, vecObbSize, vecBreakableObbSize ); MatrixAngles( matrix, vecAngles ); if ( pBreakable->VPhysicsGetObject() ) { Vector pos; pBreakable->VPhysicsGetObject()->GetPosition( &pos, NULL ); pBreakable->VPhysicsGetObject()->SetPosition( pos, vecAngles, true ); } pBreakable->SetAbsAngles( vecAngles ); if ( pOwnerEntity->IsEffectActive( EF_NOSHADOW ) ) { pBreakable->AddEffects( EF_NOSHADOW ); } } } } } if ( params.connectingParticleNames.Count() > 0 && spawnedGibs.Count() > 1 ) { const int iGibCount = spawnedGibs.Count(); int iParticlesLeft = iGibCount / 2; int iEntIndex0 = RandomInt( 0, iGibCount - 1 ); while ( iParticlesLeft > 0 ) { iParticlesLeft--; const int iEntIndex1 = ( iEntIndex0 + RandomInt( 1, iGibCount - 1 ) ) % iGibCount; CBaseEntity *pEnt0 = spawnedGibs[ iEntIndex0 ]; CBaseEntity *pEnt1 = spawnedGibs[ iEntIndex1 ]; const int iParticleSystemIndex = RandomInt( 0, params.connectingParticleNames.Count() - 1 ); DispatchParticleEffect( params.connectingParticleNames[ iParticleSystemIndex ], pEnt0, pEnt1 ); iEntIndex0 = ( iEntIndex0 + RandomInt( 1, iGibCount - 1 ) ) % iGibCount; } } }