//----------------------------------------------------------------------------- // Purpose: Remove effects immediately, including all current particles. If no // effect is specified, all effects attached to this entity are removed. //----------------------------------------------------------------------------- void CParticleProperty::StopEmissionAndDestroyImmediately( CNewParticleEffect *pEffect ) { if ( pEffect ) { int iIndex = FindEffect( pEffect ); Assert( iIndex != -1 ); if ( iIndex != -1 ) { m_ParticleEffects.Remove( iIndex ); // Clear the owner so it doesn't try to call back to us on deletion pEffect->SetOwner( NULL ); pEffect->StopEmission( false, true ); } } else { // Immediately destroy all effects int nCount = m_ParticleEffects.Count(); for ( int i = nCount-1; i >= 0; i-- ) { CNewParticleEffect *pTmp = m_ParticleEffects[i].pParticleEffect.GetObject(); m_ParticleEffects.Remove( i ); // Clear the owner so it doesn't try to call back to us on deletion pTmp->SetOwner( NULL ); pTmp->StopEmission( false, true ); } } }
//----------------------------------------------------------------------------- // Purpose: Stop effects from emitting more particles. If no effect is // specified, all effects attached to this entity are stopped. //----------------------------------------------------------------------------- void CParticleProperty::StopEmission( CNewParticleEffect *pEffect, bool bWakeOnStop, bool bDestroyAsleepSystems ) { // If we return from dormancy and are then told to stop emitting, // we should have died while dormant. Remove ourselves immediately. bool bRemoveInstantly = (m_iDormancyChangedAtFrame == gpGlobals->framecount); if ( pEffect ) { if ( FindEffect( pEffect ) != -1 ) { pEffect->StopEmission( false, bRemoveInstantly, bWakeOnStop ); } } else { // Stop all effects float flNow = g_pParticleSystemMgr->GetLastSimulationTime(); int nCount = m_ParticleEffects.Count(); for ( int i = nCount-1; i >= 0; i-- ) { CNewParticleEffect *pTmp = m_ParticleEffects[i].pParticleEffect.GetObject(); bool bRemoveSystem = bRemoveInstantly || ( bDestroyAsleepSystems && ( flNow >= pTmp->m_flNextSleepTime ) ); if ( bRemoveSystem ) { m_ParticleEffects.Remove( i ); pTmp->SetOwner( NULL ); } pTmp->StopEmission( false, bRemoveSystem, !bRemoveSystem && bWakeOnStop ); } } }
//----------------------------------------------------------------------------- // Purpose: Stop all effects that were created using the given definition // name. //----------------------------------------------------------------------------- void CParticleProperty::StopParticlesNamed( const char *pszEffectName, bool bForceRemoveInstantly /* =false */ ) { CParticleSystemDefinition *pDef = g_pParticleSystemMgr->FindParticleSystem( pszEffectName ); AssertMsg1(pDef, "Could not find particle definition %s", pszEffectName ); if (!pDef) return; // If we return from dormancy and are then told to stop emitting, // we should have died while dormant. Remove ourselves immediately. bool bRemoveInstantly = (m_iDormancyChangedAtFrame == gpGlobals->framecount); // force remove particles instantly if caller specified bRemoveInstantly |= bForceRemoveInstantly; int nCount = m_ParticleEffects.Count(); for ( int i = 0; i < nCount; ++i ) { // for each effect... CNewParticleEffect *pParticleEffect = m_ParticleEffects[i].pParticleEffect.GetObject(); if (pParticleEffect->m_pDef() == pDef) { pParticleEffect->StopEmission( false, bRemoveInstantly ); } } }
CNewParticleEffect *CParticleProperty::Create( CParticleSystemDefinition *pDef, ParticleAttachment_t iAttachType, int iAttachmentPoint, Vector vecOriginOffset, matrix3x4_t *matOffset ) { int nBatchMode = cl_particle_batch_mode.GetInt(); bool bRequestedBatch = ( nBatchMode == 2 ) || ( ( nBatchMode == 1 ) && pDef && pDef->ShouldBatch() ); if ( ( iAttachType == PATTACH_CUSTOMORIGIN ) && bRequestedBatch ) { int iIndex = FindEffect( pDef->GetName() ); if ( iIndex >= 0 ) { CNewParticleEffect *pEffect = m_ParticleEffects[iIndex].pParticleEffect.GetObject(); pEffect->Restart(); return pEffect; } } int iIndex = m_ParticleEffects.AddToTail(); ParticleEffectList_t *newEffect = &m_ParticleEffects[iIndex]; newEffect->pParticleEffect = CNewParticleEffect::Create( m_pOuter, pDef, pDef->GetName() ); if ( !newEffect->pParticleEffect->IsValid() ) { // Caused by trying to spawn an unregistered particle effect. Remove it. ParticleMgr()->RemoveEffect( newEffect->pParticleEffect.GetObject() ); return NULL; } AddControlPoint( iIndex, 0, GetOuter(), iAttachType, iAttachmentPoint, vecOriginOffset, matOffset ); if ( m_pOuter ) { m_pOuter->OnNewParticleEffect( pDef->GetName(), newEffect->pParticleEffect.GetObject() ); } return newEffect->pParticleEffect.GetObject(); }
void CClientTools::SetRecording( ParticleSystemSearchResult sr, bool bRecord ) { Assert( sr ); if ( sr == NULL ) return; CNewParticleEffect *pParticleEffect = reinterpret_cast< CNewParticleEffect* >( sr ); pParticleEffect->SetToolRecording( bRecord ); }
CNewParticleEffect *CNewParticleEffect::CreateOrAggregate( CBaseEntity *pOwner, CParticleSystemDefinition *pDef, Vector const &vecAggregatePosition, const char *pDebugName, int nSplitScreenSlot ) { CNewParticleEffect *pAggregateTarget = NULL; // see if we should aggregate bool bCanAggregate = ( pOwner == NULL ) && ( pDef->m_flAggregateRadius > 0.0 ) && ( cl_aggregate_particles.GetInt() != 0 ); if ( bCanAggregate ) { CParticleSystemDefinition *pDefFallback = pDef; do { float flAggregateDistSqr = ( pDefFallback->m_flAggregateRadius * pDefFallback->m_flAggregateRadius ) + 0.1; for( CParticleCollection *pSystem = pDefFallback->FirstCollection(); pSystem; pSystem = pSystem->GetNextCollectionUsingSameDef() ) { CNewParticleEffect *pEffectCheck = static_cast<CNewParticleEffect *>( pSystem ); if ( ! pEffectCheck->m_bDisableAggregation ) { float flDistSQ = vecAggregatePosition.DistToSqr( pEffectCheck->m_vecAggregationCenter ); if ( ( flDistSQ < flAggregateDistSqr ) && ( pSystem->m_nMaxAllowedParticles - pSystem->m_nActiveParticles > pDefFallback->m_nAggregationMinAvailableParticles ) && ( pEffectCheck->m_nSplitScreenUser == nSplitScreenSlot ) ) { flAggregateDistSqr = flDistSQ; pAggregateTarget = pEffectCheck; } } } pDefFallback = pDefFallback->GetFallbackReplacementDefinition(); } while ( pDefFallback ); } if ( ! pAggregateTarget ) { // we need a new one pAggregateTarget = new CNewParticleEffect( pOwner, pDef ); pAggregateTarget->SetDrawOnlyForSplitScreenUser( nSplitScreenSlot ); pAggregateTarget->SetDynamicallyAllocated( true ); } else { // just reset the old one pAggregateTarget->Restart( RESTART_RESET_AND_MAKE_SURE_EMITS_HAPPEN ); } if ( bCanAggregate ) { pAggregateTarget->m_vecAggregationCenter = vecAggregatePosition; } pAggregateTarget->m_pDebugName = pDebugName; pAggregateTarget->m_bDisableAggregation = false; return pAggregateTarget; }
CNewParticleEffect *CParticleProperty::Create( const char *pszParticleName, ParticleAttachment_t iAttachType, int iAttachmentPoint, Vector vecOriginOffset ) { if ( GameRules() ) { pszParticleName = GameRules()->TranslateEffectForVisionFilter( "particles", pszParticleName ); } int nBatchMode = cl_particle_batch_mode.GetInt(); CParticleSystemDefinition *pDef = g_pParticleSystemMgr->FindParticleSystem( pszParticleName ); bool bRequestedBatch = ( nBatchMode == 2 ) || ( ( nBatchMode == 1 ) && pDef && pDef->ShouldBatch() ); if ( ( iAttachType == PATTACH_CUSTOMORIGIN ) && bRequestedBatch ) { int iIndex = FindEffect( pszParticleName ); if ( iIndex >= 0 ) { CNewParticleEffect *pEffect = m_ParticleEffects[iIndex].pParticleEffect.GetObject(); pEffect->Restart(); return pEffect; } } if ( !pDef ) { AssertMsg( 0, "Attempting to create unknown particle system" ); Warning( "Attempting to create unknown particle system '%s' \n", pszParticleName ); return NULL; } int iIndex = m_ParticleEffects.AddToTail(); ParticleEffectList_t *newEffect = &m_ParticleEffects[iIndex]; newEffect->pParticleEffect = CNewParticleEffect::Create( m_pOuter, pDef ); if ( !newEffect->pParticleEffect->IsValid() ) { // Caused by trying to spawn an unregistered particle effect. Remove it. ParticleMgr()->RemoveEffect( newEffect->pParticleEffect.GetObject() ); return NULL; } AddControlPoint( iIndex, 0, GetOuter(), iAttachType, iAttachmentPoint, vecOriginOffset ); if ( m_pOuter ) { m_pOuter->OnNewParticleEffect( pszParticleName, newEffect->pParticleEffect.GetObject() ); } return newEffect->pParticleEffect.GetObject(); }
void CNewParticleEffect::RemoveParticleEffect( int nPrecacheIndex ) { CParticleSystemDefinition* pDef = g_pParticleSystemMgr->FindPrecachedParticleSystem( nPrecacheIndex ); if ( pDef == NULL ) return; for( CParticleCollection *pSystem = pDef->FirstCollection(); pSystem; pSystem = pSystem->GetNextCollectionUsingSameDef() ) { CNewParticleEffect *pEffectCheck = static_cast<CNewParticleEffect *>( pSystem ); if ( pEffectCheck ) { pEffectCheck->SetRemoveFlag(); } } }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void C_ParticleSystem::ClientThink( void ) { if ( m_bActive ) { const char *pszName = GetParticleSystemNameFromIndex( m_iEffectIndex ); if ( pszName && pszName[0] ) { CNewParticleEffect *pEffect = ParticleProp()->Create( pszName, PATTACH_ABSORIGIN_FOLLOW ); AssertMsg1( pEffect, "Particle system couldn't make %s", pszName ); if (pEffect) { for ( int i = 0 ; i < kMAXCONTROLPOINTS ; ++i ) { CBaseEntity *pOnEntity = m_hControlPointEnts[i].Get(); if ( pOnEntity ) { ParticleProp()->AddControlPoint( pEffect, i + 1, pOnEntity, PATTACH_ABSORIGIN_FOLLOW ); } AssertMsg2( m_iControlPointParents[i] >= 0 && m_iControlPointParents[i] <= kMAXCONTROLPOINTS , "Particle system specified bogus control point parent (%d) for point %d.", m_iControlPointParents[i], i ); if (m_iControlPointParents[i] != 0) { pEffect->SetControlPointParent(i+1, m_iControlPointParents[i]); } } // NOTE: What we really want here is to compare our lifetime and that of our children and see if this delta is // already past the end of it, denoting that we're finished. In that case, just destroy us and be done. -- jdw // TODO: This can go when the SkipToTime code below goes ParticleProp()->OnParticleSystemUpdated( pEffect, 0.0f ); // Skip the effect ahead if we're restarting it float flTimeDelta = gpGlobals->curtime - m_flStartTime; if ( flTimeDelta > 0.01f ) { VPROF_BUDGET( "C_ParticleSystem::ClientThink SkipToTime", "Particle Simulation" ); pEffect->SkipToTime( flTimeDelta ); } } } } }
//----------------------------------------------------------------------------- // Purpose: Output all active effects //----------------------------------------------------------------------------- void CParticleProperty::DebugPrintEffects( void ) { int nCount = m_ParticleEffects.Count(); for ( int i = 0; i < nCount; ++i ) { // for each effect... CNewParticleEffect *pParticleEffect = m_ParticleEffects[i].pParticleEffect.GetObject(); if ( !pParticleEffect ) continue; Msg( "(%d) EffectName \"%s\" Dormant? %s Emission Stopped? %s \n", i, pParticleEffect->GetEffectName(), ( pParticleEffect->m_bDormant ) ? "yes" : "no", ( pParticleEffect->m_bEmissionStopped ) ? "yes" : "no" ); } }
//----------------------------------------------------------------------------- // Bounding box //----------------------------------------------------------------------------- CNewParticleEffect* CNewParticleEffect::ReplaceWith( const char *pParticleSystemName ) { StopEmission( false, true, true ); if ( !pParticleSystemName || !pParticleSystemName[0] ) return NULL; CNewParticleEffect *pNewEffect = CNewParticleEffect::Create( GetOwner(), pParticleSystemName, pParticleSystemName ); if ( !pNewEffect ) return NULL; // Copy over the control point data for ( int i = 0; i < MAX_PARTICLE_CONTROL_POINTS; ++i ) { if ( !ReadsControlPoint( i ) ) continue; Vector vecForward, vecRight, vecUp; pNewEffect->SetControlPoint( i, GetControlPointAtCurrentTime( i ) ); GetControlPointOrientationAtCurrentTime( i, &vecForward, &vecRight, &vecUp ); pNewEffect->SetControlPointOrientation( i, vecForward, vecRight, vecUp ); pNewEffect->SetControlPointParent( i, GetControlPointParent( i ) ); } if ( m_hOwner ) { m_hOwner->ParticleProp()->ReplaceParticleEffect( this, pNewEffect ); } // fixup any other references to the old system, to point to the new system while( m_References.m_pHead ) { // this will remove the reference from m_References m_References.m_pHead->Set( pNewEffect ); } // At this point any references should have been redirected, // but we may still be running with some stray particles, so we // might not be flagged for removal - force the issue! Assert( m_RefCount == 0 ); SetRemoveFlag(); return pNewEffect; }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void C_ParticleSystem::ClientThink( void ) { if ( m_bActive ) { const char *pszName = GetParticleSystemNameFromIndex( m_iEffectIndex ); if ( pszName && pszName[0] ) { CNewParticleEffect *pEffect = ParticleProp()->Create( pszName, PATTACH_ABSORIGIN_FOLLOW ); m_pEffect = pEffect; if (pEffect) { for ( int i = 0 ; i < kMAXCONTROLPOINTS ; ++i ) { CBaseEntity *pOnEntity = m_hControlPointEnts[i].Get(); if ( pOnEntity ) { ParticleProp()->AddControlPoint( pEffect, i + 1, pOnEntity, PATTACH_ABSORIGIN_FOLLOW ); } AssertMsg2( m_iControlPointParents[i] >= 0 && m_iControlPointParents[i] <= kMAXCONTROLPOINTS , "Particle system specified bogus control point parent (%d) for point %d.", m_iControlPointParents[i], i ); if (m_iControlPointParents[i] != 0) { pEffect->SetControlPointParent(i+1, m_iControlPointParents[i]); } } //server controlled control points (variables in particle effects instead of literal follow points) for( int i = 0; i != ARRAYSIZE( m_iServerControlPointAssignments ); ++i ) { if( m_iServerControlPointAssignments[i] != 255 ) { pEffect->SetControlPoint( m_iServerControlPointAssignments[i], m_vServerControlPoints[i] ); } else { break; } } // Attach our particle snapshot if we have one Assert( m_pSnapshot || !m_szSnapshotFileName[0] ); // m_szSnapshotFileName shouldn't change after the create update if ( m_pSnapshot ) { pEffect->SetControlPointSnapshot( 0, m_pSnapshot ); } // NOTE: What we really want here is to compare our lifetime and that of our children and see if this delta is // already past the end of it, denoting that we're finished. In that case, just destroy us and be done. -- jdw // TODO: This can go when the SkipToTime code below goes ParticleProp()->OnParticleSystemUpdated( pEffect, 0.0f ); // Skip the effect ahead if we're restarting it float flTimeDelta = gpGlobals->curtime - m_flStartTime; if ( flTimeDelta > 0.01f ) { VPROF_BUDGET( "C_ParticleSystem::ClientThink SkipToTime", "Particle Simulation" ); pEffect->SkipToTime( flTimeDelta ); } } } } }
//----------------------------------------------------------------------------- // Reload particle definitions //----------------------------------------------------------------------------- void CClientTools::ReloadParticleDefintions( const char *pFileName, const void *pBufData, int nLen ) { MDLCACHE_CRITICAL_SECTION(); // Copying particle attachment control points may end up needing to evaluate skeletons ////////////// // Find any systems that depend on any system in the buffer // slow, but necessary if we want live reloads to work - and doesn't happen too often CUtlVector<CUtlString> systemNamesToReload; g_pParticleSystemMgr->GetParticleSystemsInBuffer( CUtlBuffer(pBufData, nLen, CUtlBuffer::READ_ONLY), &systemNamesToReload ); CUtlVector<CNewParticleEffect*> toReplaceEffects; CUtlVector<CUtlString> toReplaceNames; for( CNewParticleEffect *pEffect = ParticleMgr()->FirstNewEffect(); pEffect; pEffect = ParticleMgr()->NextNewEffect(pEffect) ) { for( int i = 0; i < systemNamesToReload.Count(); ++i ) { if ( pEffect->DependsOnSystem( systemNamesToReload[i] ) ) { // only reload a given effect once if ( -1 == toReplaceEffects.Find(pEffect) ) { toReplaceNames.AddToTail( pEffect->GetName() ); toReplaceEffects.AddToTail( pEffect ); } } } } CUtlVector<CNonDrawingParticleSystem*> toReplaceEffectsNonDrawing; CUtlVector<CUtlString> toReplaceNamesNonDrawing; for( CNonDrawingParticleSystem *i = ParticleMgr()->m_NonDrawingParticleSystems.Head(); i ; i = i->m_pNext ) { for( int j = 0; j < systemNamesToReload.Count(); ++j ) { if ( i->Get()->DependsOnSystem( systemNamesToReload[j] ) ) { if ( -1 == toReplaceEffectsNonDrawing.Find( i ) ) { toReplaceNamesNonDrawing.AddToTail( i->Get()->GetName() ); toReplaceEffectsNonDrawing.AddToTail( i ); } } } } ////////////// // Load the data and stomp the old definitions g_pParticleSystemMgr->ReadParticleConfigFile( CUtlBuffer(pBufData, nLen, CUtlBuffer::READ_ONLY), true ); ////////////// // Now replace all of the systems with their new versions Assert( toReplaceEffects.Count() == toReplaceNames.Count() ); for( int i = 0; i < toReplaceNames.Count(); ++i ) { CNewParticleEffect *pEffect = toReplaceEffects[i]; pEffect->ReplaceWith( toReplaceNames[i] ); } // update all the non-drawings ones for( int i = 0; i < toReplaceNamesNonDrawing.Count(); i++ ) { CNonDrawingParticleSystem *pEffect = toReplaceEffectsNonDrawing[i]; delete pEffect->m_pSystem; pEffect->m_pSystem = g_pParticleSystemMgr->CreateParticleCollection( toReplaceNamesNonDrawing[i] ); } }
//----------------------------------------------------------------------------- // Purpose: Message handler for ASWOrderUseItemFX message //----------------------------------------------------------------------------- void CASW_Hud_Squad_Hotbar::MsgFunc_ASWOrderUseItemFX( bf_read &msg ) { int iMarine = msg.ReadShort(); C_ASW_Marine *pMarine = dynamic_cast<C_ASW_Marine*>(ClientEntityList().GetEnt(iMarine)); // turn iMarine ent index into the marine if ( !pMarine ) return; int iOrderType = msg.ReadShort(); int iInventorySlot = msg.ReadShort(); Vector vecPosition; vecPosition.x = msg.ReadFloat(); vecPosition.y = msg.ReadFloat(); vecPosition.z = msg.ReadFloat(); // loops through to see if we already have an order effect for this marine StopItemFX( pMarine ); const char *pszClassName = NULL; switch( iOrderType ) { case ASW_USE_ORDER_WITH_ITEM: { // check we have an item in that slot CASW_Weapon* pWeapon = pMarine->GetASWWeapon( iInventorySlot ); if ( !pWeapon || !pWeapon->GetWeaponInfo() || !pWeapon->GetWeaponInfo()->m_bOffhandActivate ) return; pszClassName = pWeapon->GetClassname(); } break; case ASW_USE_ORDER_HACK: { pszClassName = "asw_weapon_t75"; // for now, we're using the t75 icon for hacking } break; default: { Assert( false ); // unspecified order type return; } break; } //CNewParticleEffect *pEffect = pMarine->ParticleProp()->Create( "order_use_item", PATTACH_CUSTOMORIGIN, -1, vecPosition - pMarine->GetAbsOrigin() ); CNewParticleEffect *pEffect = pMarine->ParticleProp()->Create( "order_use_item", PATTACH_ABSORIGIN ); if ( pEffect ) { pMarine->ParticleProp()->AddControlPoint( pEffect, 1, pMarine, PATTACH_CUSTOMORIGIN ); pEffect->SetControlPoint( 1, vecPosition );//vecPosition - pMarine->GetAbsOrigin() for ( int i = 0; i < NUM_USE_ITEM_ORDER_CLASSES; i++ ) { if ( pszUseItemOrderClasses[i] && !Q_strcmp ( pszUseItemOrderClasses[i] , pszClassName ) ) { pEffect->SetControlPoint( 2, Vector( i, 0, 0 ) ); break; } } HotbarOrderEffectsList_t::IndexLocalType_t iIndex = m_hHotbarOrderEffects.AddToTail(); m_hHotbarOrderEffects[iIndex].iEffectID = iMarine; m_hHotbarOrderEffects[iIndex].pEffect = pEffect; } }