void C_ParticleSmokeGrenade::Start(CParticleMgr *pParticleMgr, IPrototypeArgAccess *pArgs) { if(!pParticleMgr->AddEffect( &m_ParticleEffect, this )) return; m_SmokeTrail.Start(pParticleMgr, pArgs); m_SmokeTrail.m_ParticleLifetime = 0.5; m_SmokeTrail.SetSpawnRate(40); m_SmokeTrail.m_MinSpeed = 0; m_SmokeTrail.m_MaxSpeed = 0; m_SmokeTrail.m_StartSize = 3; m_SmokeTrail.m_EndSize = 10; m_SmokeTrail.m_SpawnRadius = 0; m_SmokeTrail.SetLocalOrigin( GetAbsOrigin() ); for(int i=0; i < NUM_MATERIAL_HANDLES; i++) { char str[256]; Q_snprintf(str, sizeof( str ), "particle/particle_smokegrenade%d", i+1); m_MaterialHandles[i] = m_ParticleEffect.FindOrAddMaterial(str); } if( m_CurrentStage == 2 ) { FillVolume(); } // Go straight into "fill volume" mode if they want. if(pArgs) { if(pArgs->FindArg("-FillVolume")) { FillVolume(); } } m_bStarted = true; SetNextClientThink( CLIENT_THINK_ALWAYS ); #if CSTRIKE_DLL C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer(); if ( pPlayer ) { pPlayer->m_SmokeGrenades.AddToTail( this ); } #endif }
void C_ParticleSmokeGrenade::Start(CParticleMgr *pParticleMgr, IPrototypeArgAccess *pArgs) { if(!pParticleMgr->AddEffect( &m_ParticleEffect, this )) return; m_LifetimeCounter = 0; m_SmokeTrail.Start(pParticleMgr, pArgs); m_SmokeTrail.m_ParticleLifetime = 0.5; m_SmokeTrail.SetSpawnRate(40); m_SmokeTrail.m_MinSpeed = 0; m_SmokeTrail.m_MaxSpeed = 0; m_SmokeTrail.m_StartSize = 3; m_SmokeTrail.m_EndSize = 10; m_SmokeTrail.m_SpawnRadius = 0; m_SmokeTrail.SetLocalOrigin( GetAbsOrigin() ); for(int i=0; i < NUM_MATERIAL_HANDLES; i++) { char str[256]; Q_snprintf(str, sizeof( str ), "particle/particle_smokegrenade%d", i+1); m_MaterialHandles[i] = m_ParticleEffect.FindOrAddMaterial(str); } if( m_CurrentStage == 2 ) { FillVolume(); } // Go straight into "fill volume" mode if they want. if(pArgs) { if(pArgs->FindArg("-FillVolume")) { FillVolume(); } } m_bStarted = true; }
void C_ParticleSmokeGrenade::UpdateSmokeTrail( float fTimeDelta ) { C_BaseEntity *pAimEnt = GetFollowedEntity(); if ( pAimEnt ) { Vector forward, right, up; // Update the smoke particle color. if(m_CurrentStage == 0) { m_SmokeTrail.m_StartColor = EngineGetLightForPoint(GetAbsOrigin()) * 0.5f; m_SmokeTrail.m_EndColor = m_SmokeTrail.m_StartColor; } // Spin the smoke trail. AngleVectors(pAimEnt->GetAbsAngles(), &forward, &right, &up); m_SmokeTrail.m_VelocityOffset = forward * 30 + GetAbsVelocity(); m_SmokeTrail.SetLocalOrigin( GetAbsOrigin() ); m_SmokeTrail.Update(fTimeDelta); } }
//----------------------------------------------------------------------------- // This is called after sending this entity's recording state //----------------------------------------------------------------------------- void C_ParticleSmokeGrenade::CleanupToolRecordingState( KeyValues *msg ) { if ( !ToolsEnabled() ) return; BaseClass::CleanupToolRecordingState( msg ); m_SmokeTrail.CleanupToolRecordingState( msg ); // Generally, this is used to allow the entity to clean up // allocated state it put into the message, but here we're going // to use it to send particle system messages because we // know the grenade has been recorded at this point if ( !clienttools->IsInRecordingMode() ) return; // NOTE: Particle system destruction message will be sent by the particle effect itself. if ( m_bVolumeFilled && GetToolParticleEffectId() == TOOLPARTICLESYSTEMID_INVALID ) { // Needed for retriggering of the smoke grenade m_bVolumeFilled = false; int nId = AllocateToolParticleEffectId(); KeyValues *msg = new KeyValues( "OldParticleSystem_Create" ); msg->SetString( "name", "C_ParticleSmokeGrenade" ); msg->SetInt( "id", nId ); msg->SetFloat( "time", gpGlobals->curtime ); KeyValues *pEmitter = msg->FindKey( "DmeSpriteEmitter", true ); pEmitter->SetInt( "count", NUM_PARTICLES_PER_DIMENSION * NUM_PARTICLES_PER_DIMENSION * NUM_PARTICLES_PER_DIMENSION ); pEmitter->SetFloat( "duration", 0 ); pEmitter->SetString( "material", "particle/particle_smokegrenade1" ); pEmitter->SetInt( "active", true ); KeyValues *pInitializers = pEmitter->FindKey( "initializers", true ); KeyValues *pPosition = pInitializers->FindKey( "DmeVoxelPositionInitializer", true ); pPosition->SetFloat( "centerx", m_SmokeBasePos.x ); pPosition->SetFloat( "centery", m_SmokeBasePos.y ); pPosition->SetFloat( "centerz", m_SmokeBasePos.z ); pPosition->SetFloat( "particlesPerDimension", m_xCount ); pPosition->SetFloat( "particleSpacing", m_SpacingRadius ); KeyValues *pLifetime = pInitializers->FindKey( "DmeRandomLifetimeInitializer", true ); pLifetime->SetFloat( "minLifetime", m_FadeEndTime ); pLifetime->SetFloat( "maxLifetime", m_FadeEndTime ); KeyValues *pVelocity = pInitializers->FindKey( "DmeAttachmentVelocityInitializer", true ); pVelocity->SetPtr( "entindex", (void*)entindex() ); pVelocity->SetFloat( "minRandomSpeed", 10 ); pVelocity->SetFloat( "maxRandomSpeed", 20 ); KeyValues *pRoll = pInitializers->FindKey( "DmeRandomRollInitializer", true ); pRoll->SetFloat( "minRoll", -6.0f ); pRoll->SetFloat( "maxRoll", 6.0f ); KeyValues *pRollSpeed = pInitializers->FindKey( "DmeRandomRollSpeedInitializer", true ); pRollSpeed->SetFloat( "minRollSpeed", -ROTATION_SPEED ); pRollSpeed->SetFloat( "maxRollSpeed", ROTATION_SPEED ); KeyValues *pColor = pInitializers->FindKey( "DmeRandomInterpolatedColorInitializer", true ); Color c1( clamp( m_MinColor.x * 255.0f, 0, 255 ), clamp( m_MinColor.y * 255.0f, 0, 255 ), clamp( m_MinColor.z * 255.0f, 0, 255 ), 255 ); Color c2( clamp( m_MaxColor.x * 255.0f, 0, 255 ), clamp( m_MaxColor.y * 255.0f, 0, 255 ), clamp( m_MaxColor.z * 255.0f, 0, 255 ), 255 ); pColor->SetColor( "color1", c1 ); pColor->SetColor( "color2", c2 ); KeyValues *pAlpha = pInitializers->FindKey( "DmeRandomAlphaInitializer", true ); pAlpha->SetInt( "minStartAlpha", 255 ); pAlpha->SetInt( "maxStartAlpha", 255 ); pAlpha->SetInt( "minEndAlpha", 0 ); pAlpha->SetInt( "maxEndAlpha", 0 ); KeyValues *pSize = pInitializers->FindKey( "DmeRandomSizeInitializer", true ); pSize->SetFloat( "minStartSize", SMOKEPARTICLE_SIZE ); pSize->SetFloat( "maxStartSize", SMOKEPARTICLE_SIZE ); pSize->SetFloat( "minEndSize", SMOKEPARTICLE_SIZE ); pSize->SetFloat( "maxEndSize", SMOKEPARTICLE_SIZE ); pInitializers->FindKey( "DmeSolidKillInitializer", true ); KeyValues *pUpdaters = pEmitter->FindKey( "updaters", true ); pUpdaters->FindKey( "DmeRollUpdater", true ); pUpdaters->FindKey( "DmeColorUpdater", true ); KeyValues *pAlphaCosineUpdater = pUpdaters->FindKey( "DmeAlphaCosineUpdater", true ); pAlphaCosineUpdater->SetFloat( "duration", m_FadeEndTime - m_FadeStartTime ); pUpdaters->FindKey( "DmeColorDynamicLightUpdater", true ); KeyValues *pSmokeGrenadeUpdater = pUpdaters->FindKey( "DmeSmokeGrenadeUpdater", true ); pSmokeGrenadeUpdater->SetFloat( "centerx", m_SmokeBasePos.x ); pSmokeGrenadeUpdater->SetFloat( "centery", m_SmokeBasePos.y ); pSmokeGrenadeUpdater->SetFloat( "centerz", m_SmokeBasePos.z ); pSmokeGrenadeUpdater->SetFloat( "particlesPerDimension", m_xCount ); pSmokeGrenadeUpdater->SetFloat( "particleSpacing", m_SpacingRadius ); pSmokeGrenadeUpdater->SetFloat( "radiusExpandTime", SMOKESPHERE_EXPAND_TIME ); pSmokeGrenadeUpdater->SetFloat( "cutoffFraction", 0.7f ); ToolFramework_PostToolMessage( HTOOLHANDLE_INVALID, msg ); msg->deleteThis(); } }
void C_ParticleSmokeGrenade::FillVolume() { m_CurrentStage = 1; m_SmokeBasePos = GetPos(); m_SmokeTrail.SetEmit(false); m_ExpandTimeCounter = m_ExpandRadius = 0; m_bVolumeFilled = true; // Spawn all of our particles. float overlap = SMOKEPARTICLE_OVERLAP; m_SpacingRadius = (SMOKEGRENADE_PARTICLERADIUS - overlap) * NUM_PARTICLES_PER_DIMENSION * 0.5f; m_xCount = m_yCount = m_zCount = NUM_PARTICLES_PER_DIMENSION; float invNumPerDimX = 1.0f / (m_xCount-1); float invNumPerDimY = 1.0f / (m_yCount-1); float invNumPerDimZ = 1.0f / (m_zCount-1); Vector vPos; for(int x=0; x < m_xCount; x++) { vPos.x = m_SmokeBasePos.x + ((float)x * invNumPerDimX) * m_SpacingRadius * 2 - m_SpacingRadius; for(int y=0; y < m_yCount; y++) { vPos.y = m_SmokeBasePos.y + ((float)y * invNumPerDimY) * m_SpacingRadius * 2 - m_SpacingRadius; for(int z=0; z < m_zCount; z++) { vPos.z = m_SmokeBasePos.z + ((float)z * invNumPerDimZ) * m_SpacingRadius * 2 - m_SpacingRadius; // Don't spawn and simulate particles that are inside a wall int contents = enginetrace->GetPointContents( vPos ); if( contents & CONTENTS_SOLID ) { continue; } if(SmokeParticleInfo *pInfo = GetSmokeParticleInfo(x,y,z)) { // MD 11/10/03: disabled this because we weren't getting coverage near the ground. // If we want it back in certain cases, we can make it a flag. /*int contents = GetWorldPointContents(vPos); if(false && (contents & CONTENTS_SOLID)) { pInfo->m_pParticle = NULL; } else */ { SmokeGrenadeParticle *pParticle = (SmokeGrenadeParticle*)m_ParticleEffect.AddParticle(sizeof(SmokeGrenadeParticle), m_MaterialHandles[rand() % NUM_MATERIAL_HANDLES]); if(pParticle) { pParticle->m_Pos = vPos - m_SmokeBasePos; // store its position in local space pParticle->m_ColorInterp = (unsigned char)((rand() * 255) / RAND_MAX); pParticle->m_RotationSpeed = FRand(-ROTATION_SPEED, ROTATION_SPEED); // Rotation speed. pParticle->m_CurRotation = FRand(-6, 6); } #ifdef _DEBUG int testX, testY, testZ; int index = GetSmokeParticleIndex(x,y,z); GetParticleInfoXYZ(index, testX, testY, testZ); assert(testX == x && testY == y && testZ == z); #endif Vector vColor = EngineGetLightForPoint(vPos); pInfo->m_Color[0] = (unsigned char)(vColor.x * 255.9f); pInfo->m_Color[1] = (unsigned char)(vColor.y * 255.9f); pInfo->m_Color[2] = (unsigned char)(vColor.z * 255.9f); // Cast some rays and if it's too close to anything, fade its alpha down. pInfo->m_FadeAlpha = 1; /*for(int i=0; i < NUM_FADE_PLANES; i++) { trace_t trace; WorldTraceLine(vPos, vPos + s_FadePlaneDirections[i] * 100, MASK_SOLID_BRUSHONLY, &trace); if(trace.fraction < 1.0f) { float dist = DotProduct(trace.plane.normal, vPos) - trace.plane.dist; if(dist < 0) { pInfo->m_FadeAlpha = 0; } else if(dist < SMOKEPARTICLE_SIZE) { float alphaScale = dist / SMOKEPARTICLE_SIZE; alphaScale *= alphaScale * alphaScale; pInfo->m_FadeAlpha *= alphaScale; } } }*/ pInfo->m_pParticle = pParticle; pInfo->m_TradeIndex = -1; } } } } } }
void C_ParticleSmokeGrenade::Update(float fTimeDelta) { m_LifetimeCounter += fTimeDelta; // Update the smoke trail. C_BaseEntity *pAimEnt = GetFollowedEntity(); if ( pAimEnt ) { Vector forward, right, up; // Update the smoke particle color. if(m_CurrentStage == 0) { m_SmokeTrail.m_StartColor = EngineGetLightForPoint(GetAbsOrigin()) * 0.5f; m_SmokeTrail.m_EndColor = m_SmokeTrail.m_StartColor; } // Spin the smoke trail. AngleVectors(pAimEnt->GetAbsAngles(), &forward, &right, &up); m_SmokeTrail.m_VelocityOffset = forward * 30 + GetAbsVelocity(); m_SmokeTrail.SetLocalOrigin( GetAbsOrigin() ); m_SmokeTrail.Update(fTimeDelta); } // Update our fade alpha. if(m_LifetimeCounter < m_FadeStartTime) { m_FadeAlpha = 1; } else if(m_LifetimeCounter < m_FadeEndTime) { float fadePercent = (m_LifetimeCounter - m_FadeStartTime) / (m_FadeEndTime - m_FadeStartTime); m_FadeAlpha = cos(fadePercent * 3.14159) * 0.5 + 0.5; } else { m_FadeAlpha = 0; } // Scale by the amount the sphere has grown. m_FadeAlpha *= m_ExpandRadius / SMOKESPHERE_MAX_RADIUS; if(m_CurrentStage == 1) { // Update the expanding sphere. m_ExpandTimeCounter += fTimeDelta; if(m_ExpandTimeCounter > SMOKESPHERE_EXPAND_TIME) m_ExpandTimeCounter = SMOKESPHERE_EXPAND_TIME; m_ExpandRadius = SMOKESPHERE_MAX_RADIUS * (float)sin(m_ExpandTimeCounter * 3.14159265358 * 0.5 / SMOKESPHERE_EXPAND_TIME); // Add our influence to the global smoke fog alpha. float testDist = (EngineGetVecRenderOrigin() - m_SmokeBasePos).Length(); float fadeEnd = m_ExpandRadius * 0.75; if(testDist < fadeEnd) { EngineGetSmokeFogOverlayAlpha() += 1 - testDist / fadeEnd; } // This is used to randomize the direction it chooses to move a particle in. int offsetLookup[3] = {-1,0,1}; // Update all the moving traders and establish new ones. int nTotal = m_xCount * m_yCount * m_zCount; for(int i=0; i < nTotal; i++) { SmokeParticleInfo *pInfo = &m_SmokeParticleInfos[i]; if(!pInfo->m_pParticle) continue; if(pInfo->m_TradeIndex == -1) { pInfo->m_pParticle->m_FadeAlpha = pInfo->m_FadeAlpha; pInfo->m_pParticle->m_Color[0] = pInfo->m_Color[0]; pInfo->m_pParticle->m_Color[1] = pInfo->m_Color[1]; pInfo->m_pParticle->m_Color[2] = pInfo->m_Color[2]; // Is there an adjacent one that's not trading? int x, y, z; GetParticleInfoXYZ(i, x, y, z); int xCountOffset = rand(); int yCountOffset = rand(); int zCountOffset = rand(); bool bFound = false; for(int xCount=0; xCount < 3 && !bFound; xCount++) { for(int yCount=0; yCount < 3 && !bFound; yCount++) { for(int zCount=0; zCount < 3; zCount++) { int testX = x + offsetLookup[(xCount+xCountOffset) % 3]; int testY = y + offsetLookup[(yCount+yCountOffset) % 3]; int testZ = z + offsetLookup[(zCount+zCountOffset) % 3]; if(testX == x && testY == y && testZ == z) continue; if(IsValidXYZCoords(testX, testY, testZ)) { SmokeParticleInfo *pOther = GetSmokeParticleInfo(testX, testY, testZ); if(pOther->m_pParticle && pOther->m_TradeIndex == -1) { // Ok, this one is looking to trade also. pInfo->m_TradeIndex = GetSmokeParticleIndex(testX, testY, testZ); pOther->m_TradeIndex = i; pInfo->m_TradeClock = pOther->m_TradeClock = 0; pInfo->m_TradeDuration = FRand(TRADE_DURATION_MIN, TRADE_DURATION_MAX); bFound = true; break; } } } } } } else { SmokeParticleInfo *pOther = &m_SmokeParticleInfos[pInfo->m_TradeIndex]; assert(pOther->m_TradeIndex == i); // This makes sure the trade only gets updated once per frame. if(pInfo < pOther) { // Increment the trade clock.. pInfo->m_TradeClock = (pOther->m_TradeClock += fTimeDelta); int x, y, z; GetParticleInfoXYZ(i, x, y, z); Vector myPos = GetSmokeParticlePos(x, y, z); int otherX, otherY, otherZ; GetParticleInfoXYZ(pInfo->m_TradeIndex, otherX, otherY, otherZ); Vector otherPos = GetSmokeParticlePos(otherX, otherY, otherZ); // Is the trade finished? if(pInfo->m_TradeClock >= pInfo->m_TradeDuration) { pInfo->m_TradeIndex = pOther->m_TradeIndex = -1; pInfo->m_pParticle->m_Pos = otherPos; pOther->m_pParticle->m_Pos = myPos; SmokeGrenadeParticle *temp = pInfo->m_pParticle; pInfo->m_pParticle = pOther->m_pParticle; pOther->m_pParticle = temp; } else { // Ok, move them closer. float percent = (float)cos(pInfo->m_TradeClock * 2 * 1.57079632f / pInfo->m_TradeDuration); percent = percent * 0.5 + 0.5; pInfo->m_pParticle->m_FadeAlpha = pInfo->m_FadeAlpha + (pOther->m_FadeAlpha - pInfo->m_FadeAlpha) * (1 - percent); pOther->m_pParticle->m_FadeAlpha = pInfo->m_FadeAlpha + (pOther->m_FadeAlpha - pInfo->m_FadeAlpha) * percent; InterpColor(pInfo->m_pParticle->m_Color, pInfo->m_Color, pOther->m_Color, 1-percent); InterpColor(pOther->m_pParticle->m_Color, pInfo->m_Color, pOther->m_Color, percent); pInfo->m_pParticle->m_Pos = myPos + (otherPos - myPos) * (1 - percent); pOther->m_pParticle->m_Pos = myPos + (otherPos - myPos) * percent; } } } } } }