//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// // // Purpose: // // $NoKeywords: $ //===========================================================================// #include "cbase.h" #include "iviewrender.h" #include "view.h" #include "studio.h" #include "bone_setup.h" #include "model_types.h" #include "beamdraw.h" #include "engine/ivdebugoverlay.h" #include "iviewrender_beams.h" #include "fx.h" #include "IEffects.h" #include "c_entitydissolve.h" #include "movevars_shared.h" #include "precache_register.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" ConVar cl_portal_use_new_dissolve( "cl_portal_use_new_dissolve", "1", FCVAR_CHEAT, "Use new dissolve effect" ); PRECACHE_REGISTER_BEGIN( GLOBAL, PrecacheEffectBuild ) PRECACHE( MATERIAL,"effects/tesla_glow_noz" ) PRECACHE( MATERIAL,"effects/spark" ) PRECACHE( MATERIAL,"effects/combinemuzzle2" ) PRECACHE( PARTICLE_SYSTEM, "dissolve" ) PRECACHE_REGISTER_END() //----------------------------------------------------------------------------- // Networking //----------------------------------------------------------------------------- IMPLEMENT_CLIENTCLASS_DT( C_EntityDissolve, DT_EntityDissolve, CEntityDissolve ) RecvPropTime(RECVINFO(m_flStartTime)), RecvPropFloat(RECVINFO(m_flFadeOutStart)), RecvPropFloat(RECVINFO(m_flFadeOutLength)), RecvPropFloat(RECVINFO(m_flFadeOutModelStart)), RecvPropFloat(RECVINFO(m_flFadeOutModelLength)), RecvPropFloat(RECVINFO(m_flFadeInStart)), RecvPropFloat(RECVINFO(m_flFadeInLength)), RecvPropInt(RECVINFO(m_nDissolveType)), RecvPropVector( RECVINFO( m_vDissolverOrigin) ), RecvPropInt( RECVINFO( m_nMagnitude ) ), END_RECV_TABLE() extern PMaterialHandle g_Material_Spark; PMaterialHandle g_Material_AR2Glow = NULL; //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CEG_NOINLINE C_EntityDissolve::C_EntityDissolve( void ) { m_bLinkedToServerEnt = true; CEG_PROTECT_MEMBER_FUNCTION( C_EntityDissolve_C_EntityDissolve ); m_pController = NULL; m_bCoreExplode = false; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void C_EntityDissolve::GetRenderBounds( Vector& theMins, Vector& theMaxs ) { if ( GetMoveParent() ) { GetMoveParent()->GetRenderBounds( theMins, theMaxs ); } else { theMins = GetAbsOrigin(); theMaxs = theMaxs; } } //----------------------------------------------------------------------------- // On data changed //----------------------------------------------------------------------------- void C_EntityDissolve::OnDataChanged( DataUpdateType_t updateType ) { BaseClass::OnDataChanged( updateType ); if ( updateType == DATA_UPDATE_CREATED ) { m_flNextSparkTime = m_flStartTime; SetNextClientThink( CLIENT_THINK_ALWAYS ); if ( cl_portal_use_new_dissolve.GetBool() ) DispatchParticleEffect( "dissolve", PATTACH_ABSORIGIN_FOLLOW, GetMoveParent() ); } } //----------------------------------------------------------------------------- // Cleanup //----------------------------------------------------------------------------- void C_EntityDissolve::UpdateOnRemove( void ) { if ( m_pController ) { physenv->DestroyMotionController( m_pController ); m_pController = NULL; } BaseClass::UpdateOnRemove(); } //------------------------------------------------------------------------------ // Apply the forces to the entity //------------------------------------------------------------------------------ IMotionEvent::simresult_e C_EntityDissolve::Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject, float deltaTime, Vector &linear, AngularImpulse &angular ) { linear.Init(); angular.Init(); // Make it zero g linear.z -= -1.02 * sv_gravity.GetFloat(); Vector vel; AngularImpulse angVel; pObject->GetVelocity( &vel, &angVel ); vel += linear * deltaTime; // account for gravity scale Vector unitVel = vel; Vector unitAngVel = angVel; float speed = VectorNormalize( unitVel ); // float angSpeed = VectorNormalize( unitAngVel ); // float speedScale = 0.0; // float angSpeedScale = 0.0; float flLinearLimit = 50; float flLinearLimitDelta = 40; if ( speed > flLinearLimit ) { float flDeltaVel = (flLinearLimit - speed) / deltaTime; if ( flLinearLimitDelta != 0.0f ) { float flMaxDeltaVel = -flLinearLimitDelta / deltaTime; if ( flDeltaVel < flMaxDeltaVel ) { flDeltaVel = flMaxDeltaVel; } } VectorMA( linear, flDeltaVel, unitVel, linear ); } return SIM_GLOBAL_ACCELERATION; } //----------------------------------------------------------------------------- // Tesla effect //----------------------------------------------------------------------------- static void FX_BuildTesla( C_BaseEntity *pEntity, Vector &vecOrigin, Vector &vecEnd ) { BeamInfo_t beamInfo; beamInfo.m_pStartEnt = pEntity; beamInfo.m_nStartAttachment = 0; beamInfo.m_pEndEnt = NULL; beamInfo.m_nEndAttachment = 0; beamInfo.m_nType = TE_BEAMTESLA; beamInfo.m_vecStart = vecOrigin; beamInfo.m_vecEnd = vecEnd; beamInfo.m_pszModelName = "sprites/lgtning.vmt"; beamInfo.m_flHaloScale = 0.0; beamInfo.m_flLife = random->RandomFloat( 0.25f, 1.0f ); beamInfo.m_flWidth = random->RandomFloat( 8.0f, 14.0f ); beamInfo.m_flEndWidth = 1.0f; beamInfo.m_flFadeLength = 0.5f; beamInfo.m_flAmplitude = 24; beamInfo.m_flBrightness = 255.0; beamInfo.m_flSpeed = 150.0f; beamInfo.m_nStartFrame = 0.0; beamInfo.m_flFrameRate = 30.0; beamInfo.m_flRed = 255.0; beamInfo.m_flGreen = 255.0; beamInfo.m_flBlue = 255.0; beamInfo.m_nSegments = 18; beamInfo.m_bRenderable = true; beamInfo.m_nFlags = 0; //FBEAM_ONLYNOISEONCE; beams->CreateBeamEntPoint( beamInfo ); } //----------------------------------------------------------------------------- // Purpose: Tesla effect //----------------------------------------------------------------------------- void C_EntityDissolve::BuildTeslaEffect( mstudiobbox_t *pHitBox, const matrix3x4_t &hitboxToWorld, bool bRandom, float flYawOffset ) { Vector vecOrigin; QAngle vecAngles; MatrixGetColumn( hitboxToWorld, 3, vecOrigin ); MatrixAngles( hitboxToWorld, vecAngles.Base() ); C_BaseEntity *pEntity = GetMoveParent(); // Make a couple of tries at it int iTries = -1; Vector vecForward; trace_t tr; do { iTries++; // Some beams are deliberatly aimed around the point, the rest are random. if ( !bRandom ) { QAngle vecTemp = vecAngles; vecTemp[YAW] += flYawOffset; AngleVectors( vecTemp, &vecForward ); // Randomly angle it up or down vecForward.z = RandomFloat( -1, 1 ); } else { vecForward = RandomVector( -1, 1 ); } UTIL_TraceLine( vecOrigin, vecOrigin + (vecForward * 192), MASK_SHOT, pEntity, COLLISION_GROUP_NONE, &tr ); } while ( tr.fraction >= 1.0 && iTries < 3 ); Vector vecEnd = tr.endpos - (vecForward * 8); // Only spark & glow if we hit something if ( tr.fraction < 1.0 ) { if ( !EffectOccluded( tr.endpos ) ) { int nSlot = GET_ACTIVE_SPLITSCREEN_SLOT(); // Move it towards the camera Vector vecFlash = tr.endpos; Vector vecForward; AngleVectors( MainViewAngles(nSlot), &vecForward ); vecFlash -= (vecForward * 8); g_pEffects->EnergySplash( vecFlash, -vecForward, false ); // End glow CSmartPtr pSimple = CSimpleEmitter::Create( "dust" ); pSimple->SetSortOrigin( vecFlash ); SimpleParticle *pParticle; pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), pSimple->GetPMaterial( "effects/tesla_glow_noz" ), vecFlash ); if ( pParticle != NULL ) { pParticle->m_flLifetime = 0.0f; pParticle->m_flDieTime = RandomFloat( 0.5, 1 ); pParticle->m_vecVelocity = vec3_origin; Vector color( 1,1,1 ); float colorRamp = RandomFloat( 0.75f, 1.25f ); pParticle->m_uchColor[0] = MIN( 1.0f, color[0] * colorRamp ) * 255.0f; pParticle->m_uchColor[1] = MIN( 1.0f, color[1] * colorRamp ) * 255.0f; pParticle->m_uchColor[2] = MIN( 1.0f, color[2] * colorRamp ) * 255.0f; pParticle->m_uchStartSize = RandomFloat( 6,13 ); pParticle->m_uchEndSize = pParticle->m_uchStartSize - 2; pParticle->m_uchStartAlpha = 255; pParticle->m_uchEndAlpha = 10; pParticle->m_flRoll = RandomFloat( 0,360 ); pParticle->m_flRollDelta = 0; } } } // Build the tesla FX_BuildTesla( pEntity, vecOrigin, tr.endpos ); } //----------------------------------------------------------------------------- // Sorts the components of a vector //----------------------------------------------------------------------------- static inline void SortAbsVectorComponents( const Vector& src, int* pVecIdx ) { Vector absVec( fabs(src[0]), fabs(src[1]), fabs(src[2]) ); int maxIdx = (absVec[0] > absVec[1]) ? 0 : 1; if (absVec[2] > absVec[maxIdx]) { maxIdx = 2; } // always choose something right-handed.... switch( maxIdx ) { case 0: pVecIdx[0] = 1; pVecIdx[1] = 2; pVecIdx[2] = 0; break; case 1: pVecIdx[0] = 2; pVecIdx[1] = 0; pVecIdx[2] = 1; break; case 2: pVecIdx[0] = 0; pVecIdx[1] = 1; pVecIdx[2] = 2; break; } } //----------------------------------------------------------------------------- // Compute the bounding box's center, size, and basis //----------------------------------------------------------------------------- void C_EntityDissolve::ComputeRenderInfo( mstudiobbox_t *pHitBox, const matrix3x4_t &hitboxToWorld, Vector *pVecAbsOrigin, Vector *pXVec, Vector *pYVec ) { // Compute the center of the hitbox in worldspace Vector vecHitboxCenter; VectorAdd( pHitBox->bbmin, pHitBox->bbmax, vecHitboxCenter ); vecHitboxCenter *= 0.5f; VectorTransform( vecHitboxCenter, hitboxToWorld, *pVecAbsOrigin ); // Get the object's basis Vector vec[3]; MatrixGetColumn( hitboxToWorld, 0, vec[0] ); MatrixGetColumn( hitboxToWorld, 1, vec[1] ); MatrixGetColumn( hitboxToWorld, 2, vec[2] ); // vec[1] *= -1.0f; Vector vecViewDir; VectorSubtract( CurrentViewOrigin(), *pVecAbsOrigin, vecViewDir ); VectorNormalize( vecViewDir ); // Project the shadow casting direction into the space of the hitbox Vector localViewDir; localViewDir[0] = DotProduct( vec[0], vecViewDir ); localViewDir[1] = DotProduct( vec[1], vecViewDir ); localViewDir[2] = DotProduct( vec[2], vecViewDir ); // Figure out which vector has the largest component perpendicular // to the view direction... // Sort by how perpendicular it is int vecIdx[3]; SortAbsVectorComponents( localViewDir, vecIdx ); // Here's our hitbox basis vectors; namely the ones that are // most perpendicular to the view direction *pXVec = vec[vecIdx[0]]; *pYVec = vec[vecIdx[1]]; // Project them into a plane perpendicular to the view direction *pXVec -= vecViewDir * DotProduct( vecViewDir, *pXVec ); *pYVec -= vecViewDir * DotProduct( vecViewDir, *pYVec ); VectorNormalize( *pXVec ); VectorNormalize( *pYVec ); // Compute the hitbox size Vector boxSize; VectorSubtract( pHitBox->bbmax, pHitBox->bbmin, boxSize ); // We project the two longest sides into the vectors perpendicular // to the projection direction, then add in the projection of the perp direction Vector2D size( boxSize[vecIdx[0]], boxSize[vecIdx[1]] ); size.x *= fabs( DotProduct( vec[vecIdx[0]], *pXVec ) ); size.y *= fabs( DotProduct( vec[vecIdx[1]], *pYVec ) ); // Add the third component into x and y size.x += boxSize[vecIdx[2]] * fabs( DotProduct( vec[vecIdx[2]], *pXVec ) ); size.y += boxSize[vecIdx[2]] * fabs( DotProduct( vec[vecIdx[2]], *pYVec ) ); // Bloat a bit, since the shadow wants to extend outside the model a bit size *= 2.0f; // Clamp the minimum size Vector2DMax( size, Vector2D(10.0f, 10.0f), size ); // Factor the size into the xvec + yvec (*pXVec) *= size.x * 0.5f; (*pYVec) *= size.y * 0.5f; } //----------------------------------------------------------------------------- // Sparks! //----------------------------------------------------------------------------- void C_EntityDissolve::DoSparks( mstudiohitboxset_t *set, matrix3x4_t *hitboxbones[MAXSTUDIOBONES] ) { if ( m_flNextSparkTime > gpGlobals->curtime ) return; float dt = m_flStartTime + m_flFadeOutStart - gpGlobals->curtime; dt = clamp( dt, 0.0f, m_flFadeOutStart ); float flNextTime; if (m_nDissolveType == ENTITY_DISSOLVE_ELECTRICAL) { flNextTime = SimpleSplineRemapVal( dt, 0.0f, m_flFadeOutStart, 2.0f * TICK_INTERVAL, 0.4f ); } else { // m_nDissolveType == ENTITY_DISSOLVE_ELECTRICAL_LIGHT); flNextTime = SimpleSplineRemapVal( dt, 0.0f, m_flFadeOutStart, 0.3f, 1.0f ); } m_flNextSparkTime = gpGlobals->curtime + flNextTime; // Send out beams around us int iNumBeamsAround = 2; int iNumRandomBeams = 1; int iTotalBeams = iNumBeamsAround + iNumRandomBeams; float flYawOffset = RandomFloat(0,360); for ( int i = 0; i < iTotalBeams; i++ ) { int nHitbox = random->RandomInt( 0, set->numhitboxes - 1 ); mstudiobbox_t *pBox = set->pHitbox(nHitbox); float flActualYawOffset = 0; bool bRandom = ( i >= iNumBeamsAround ); if ( !bRandom ) { flActualYawOffset = anglemod( flYawOffset + ((360 / iTotalBeams) * i) ); } BuildTeslaEffect( pBox, *hitboxbones[pBox->bone], bRandom, flActualYawOffset ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void C_EntityDissolve::SetupEmitter( void ) { if ( !m_pEmitter ) { m_pEmitter = CSimpleEmitter::Create( "C_EntityDissolve" ); m_pEmitter->SetSortOrigin( GetAbsOrigin() ); } } //----------------------------------------------------------------------------- // Purpose: // Output : float //----------------------------------------------------------------------------- float C_EntityDissolve::GetFadeInPercentage( void ) { float dt = gpGlobals->curtime - m_flStartTime; if ( dt > m_flFadeOutStart ) return 1.0f; if ( dt < m_flFadeInStart ) return 0.0f; if ( (dt > m_flFadeInStart) && (dt < m_flFadeInStart + m_flFadeInLength) ) { dt -= m_flFadeInStart; return ( dt / m_flFadeInLength ); } return 1.0f; } //----------------------------------------------------------------------------- // Purpose: // Output : float //----------------------------------------------------------------------------- float C_EntityDissolve::GetFadeOutPercentage( void ) { float dt = gpGlobals->curtime - m_flStartTime; if ( dt < m_flFadeInStart ) return 1.0f; if ( dt > m_flFadeOutStart ) { dt -= m_flFadeOutStart; RANDOM_CEG_TEST_SECRET(); if ( dt > m_flFadeOutLength ) return 0.0f; return 1.0f - ( dt / m_flFadeOutLength ); } return 1.0f; } //----------------------------------------------------------------------------- // Purpose: // Output : float //----------------------------------------------------------------------------- float C_EntityDissolve::GetModelFadeOutPercentage( void ) { float dt = gpGlobals->curtime - m_flStartTime; if ( dt < m_flFadeOutModelStart ) return 1.0f; if ( dt > m_flFadeOutModelStart ) { dt -= m_flFadeOutModelStart; if ( dt > m_flFadeOutModelLength ) return 0.0f; return 1.0f - ( dt / m_flFadeOutModelLength ); } return 1.0f; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void C_EntityDissolve::ClientThink( void ) { C_BaseAnimating *pAnimating = GetMoveParent() ? GetMoveParent()->GetBaseAnimating() : NULL; if (!pAnimating) return; // NOTE: IsRagdoll means *client-side* ragdoll. We shouldn't be trying to fight // the server ragdoll (or any server physics) on the client if (( !m_pController ) && ( m_nDissolveType == ENTITY_DISSOLVE_NORMAL ) && pAnimating->IsRagdoll()) { IPhysicsObject *ppList[VPHYSICS_MAX_OBJECT_LIST_COUNT]; int nCount = pAnimating->VPhysicsGetObjectList( ppList, ARRAYSIZE(ppList) ); if ( nCount > 0 ) { m_pController = physenv->CreateMotionController( this ); for ( int i = 0; i < nCount; ++i ) { m_pController->AttachObject( ppList[i], true ); } } } color32 color; color.r = color.g = color.b = ( 1.0f - GetFadeInPercentage() ) * 255.0f; color.a = GetModelFadeOutPercentage() * 255.0f; // Setup the entity fade pAnimating->SetRenderMode( kRenderTransColor ); pAnimating->SetRenderColor( color.r, color.g, color.b ); pAnimating->SetRenderAlpha( color.a ); if ( GetModelFadeOutPercentage() <= 0.2f ) { m_bCoreExplode = true; } // If we're dead, fade out if ( GetFadeOutPercentage() <= 0.0f ) { // Do NOT remove from the client entity list. It'll confuse the local network backdoor, and the entity will never get destroyed // because when the server says to destroy it, the client won't be able to find it. // ClientEntityList().RemoveEntity( GetClientHandle() ); ::partition->Remove( PARTITION_CLIENT_SOLID_EDICTS | PARTITION_CLIENT_RESPONSIVE_EDICTS | PARTITION_CLIENT_NON_STATIC_EDICTS, CollisionProp()->GetPartitionHandle() ); RemoveFromLeafSystem(); //FIXME: Ick! //Adrian: I'll assume we don't need the ragdoll either so I'll remove that too. if ( m_bLinkedToServerEnt == false ) { Release(); C_ClientRagdoll *pRagdoll = dynamic_cast ( pAnimating ); if ( pRagdoll ) { pRagdoll->ReleaseRagdoll(); } } } } //----------------------------------------------------------------------------- // Purpose: // Input : flags - // Output : int //----------------------------------------------------------------------------- int C_EntityDissolve::DrawModel( int flags, const RenderableInstance_t &instance ) { if ( !cl_portal_use_new_dissolve.GetBool() ) { // See if we should draw if ( gpGlobals->frametime == 0 || m_bReadyToDraw == false ) return 0; C_BaseAnimating *pAnimating = GetMoveParent() ? GetMoveParent()->GetBaseAnimating() : NULL; if ( pAnimating == NULL ) return 0; matrix3x4_t *hitboxbones[MAXSTUDIOBONES]; if ( pAnimating->HitboxToWorldTransforms( hitboxbones ) == false ) return 0; studiohdr_t *pStudioHdr = modelinfo->GetStudiomodel( pAnimating->GetModel() ); if ( pStudioHdr == NULL ) return false; mstudiohitboxset_t *set = pStudioHdr->pHitboxSet( pAnimating->GetHitboxSet() ); if ( set == NULL ) return false; // Make sure the emitter is setup properly SetupEmitter(); // Get fade percentages for the effect float fadeInPerc = GetFadeInPercentage(); float fadeOutPerc = GetFadeOutPercentage(); float fadePerc = ( fadeInPerc >= 1.0f ) ? fadeOutPerc : fadeInPerc; Vector vecSkew = vec3_origin; // Do extra effects under certain circumstances if ( ( fadePerc < 0.99f ) && ( (m_nDissolveType == ENTITY_DISSOLVE_ELECTRICAL) || (m_nDissolveType == ENTITY_DISSOLVE_ELECTRICAL_LIGHT) ) ) { DoSparks( set, hitboxbones ); } // Skew the particles in front or in back of their targets vecSkew = CurrentViewForward() * ( 8.0f - ( ( 1.0f - fadePerc ) * 32.0f ) ); float spriteScale = ( ( gpGlobals->curtime - m_flStartTime ) / m_flFadeOutLength ); spriteScale = clamp( spriteScale, 0.75f, 1.0f ); // Cache off this material reference if ( g_Material_Spark == NULL ) { g_Material_Spark = ParticleMgr()->GetPMaterial( "effects/spark" ); } if ( g_Material_AR2Glow == NULL ) { g_Material_AR2Glow = ParticleMgr()->GetPMaterial( "effects/combinemuzzle2" ); } SimpleParticle *sParticle; for ( int i = 0; i < set->numhitboxes; ++i ) { Vector vecAbsOrigin, xvec, yvec; mstudiobbox_t *pBox = set->pHitbox(i); ComputeRenderInfo( pBox, *hitboxbones[pBox->bone], &vecAbsOrigin, &xvec, &yvec ); Vector offset; Vector xDir, yDir; xDir = xvec; float xScale = VectorNormalize( xDir ) * 0.75f; yDir = yvec; float yScale = VectorNormalize( yDir ) * 0.75f; int numParticles = clamp( 3.0f * fadePerc, 0, 3 ); int iTempParts = 2; if ( m_nDissolveType == ENTITY_DISSOLVE_CORE ) { if ( m_bCoreExplode == true ) { numParticles = 15; iTempParts = 20; } } for ( int j = 0; j < iTempParts; j++ ) { // Skew the origin offset = xDir * Helper_RandomFloat( -xScale*0.5f, xScale*0.5f ) + yDir * Helper_RandomFloat( -yScale*0.5f, yScale*0.5f ); offset += vecSkew; if ( random->RandomInt( 0, 2 ) != 0 ) continue; sParticle = (SimpleParticle *) m_pEmitter->AddParticle( sizeof(SimpleParticle), g_Material_Spark, vecAbsOrigin + offset ); if ( sParticle == NULL ) return 1; sParticle->m_vecVelocity = Vector( Helper_RandomFloat( -4.0f, 4.0f ), Helper_RandomFloat( -4.0f, 4.0f ), Helper_RandomFloat( 16.0f, 64.0f ) ); if ( m_nDissolveType == ENTITY_DISSOLVE_CORE ) { if ( m_bCoreExplode == true ) { Vector vDirection = (vecAbsOrigin + offset) - m_vDissolverOrigin; VectorNormalize( vDirection ); sParticle->m_vecVelocity = vDirection * m_nMagnitude; } } if ( sParticle->m_vecVelocity.z > 0 ) { sParticle->m_uchStartSize = random->RandomFloat( 4, 6 ) * spriteScale; } else { sParticle->m_uchStartSize = 2 * spriteScale; } sParticle->m_flDieTime = random->RandomFloat( 0.4f, 0.5f ); // If we're the last particles, last longer if ( numParticles == 0 ) { sParticle->m_flDieTime *= 2.0f; sParticle->m_uchStartSize = 2 * spriteScale; sParticle->m_flRollDelta = Helper_RandomFloat( -4.0f, 4.0f ); if ( m_nDissolveType == ENTITY_DISSOLVE_CORE ) { if ( m_bCoreExplode == true ) { sParticle->m_flDieTime *= 2.0f; sParticle->m_flRollDelta = Helper_RandomFloat( -1.0f, 1.0f ); } } } else { sParticle->m_flRollDelta = Helper_RandomFloat( -8.0f, 8.0f ); } sParticle->m_flLifetime = 0.0f; sParticle->m_flRoll = Helper_RandomInt( 0, 360 ); float alpha = 255; sParticle->m_uchColor[0] = alpha; sParticle->m_uchColor[1] = alpha; sParticle->m_uchColor[2] = alpha; sParticle->m_uchStartAlpha = alpha; sParticle->m_uchEndAlpha = 0; sParticle->m_uchEndSize = 0; } for ( int j = 0; j < numParticles; j++ ) { offset = xDir * Helper_RandomFloat( -xScale*0.5f, xScale*0.5f ) + yDir * Helper_RandomFloat( -yScale*0.5f, yScale*0.5f ); offset += vecSkew; sParticle = (SimpleParticle *) m_pEmitter->AddParticle( sizeof(SimpleParticle), g_Material_AR2Glow, vecAbsOrigin + offset ); if ( sParticle == NULL ) return 1; sParticle->m_vecVelocity = Vector( Helper_RandomFloat( -4.0f, 4.0f ), Helper_RandomFloat( -4.0f, 4.0f ), Helper_RandomFloat( -64.0f, 128.0f ) ); sParticle->m_uchStartSize = random->RandomFloat( 8, 12 ) * spriteScale; sParticle->m_flDieTime = 0.1f; sParticle->m_flLifetime = 0.0f; sParticle->m_flRoll = Helper_RandomInt( 0, 360 ); sParticle->m_flRollDelta = Helper_RandomFloat( -2.0f, 2.0f ); float alpha = 255; sParticle->m_uchColor[0] = alpha; sParticle->m_uchColor[1] = alpha; sParticle->m_uchColor[2] = alpha; sParticle->m_uchStartAlpha = alpha; sParticle->m_uchEndAlpha = 0; sParticle->m_uchEndSize = 0; if ( m_nDissolveType == ENTITY_DISSOLVE_CORE ) { if ( m_bCoreExplode == true ) { Vector vDirection = (vecAbsOrigin + offset) - m_vDissolverOrigin; VectorNormalize( vDirection ); sParticle->m_vecVelocity = vDirection * m_nMagnitude; sParticle->m_flDieTime = 0.5f; } } } } } return 1; }