//====================================================================================================================== // PARTICLE SYSTEM DISPATCH EFFECT //====================================================================================================================== //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void ParticleEffectCallback( const CEffectData &data ) { if ( SuppressingParticleEffects() ) return; // this needs to be before using data.m_nHitBox, since that may be a serialized value that's past the end of the current particle system string table const char *pszName = GetParticleSystemNameFromIndex( data.m_nHitBox ); if ( data.m_fFlags & PARTICLE_DISPATCH_FROM_ENTITY ) { if ( data.m_hEntity.Get() ) { C_BaseEntity *pEnt = C_BaseEntity::Instance( data.m_hEntity ); if ( pEnt && !pEnt->IsDormant() ) { if ( data.m_fFlags & PARTICLE_DISPATCH_RESET_PARTICLES ) { pEnt->ParticleProp()->StopEmission(); } CSmartPtr<CNewParticleEffect> pEffect = pEnt->ParticleProp()->Create( pszName, (ParticleAttachment_t)data.m_nDamageType, data.m_nAttachmentIndex ); AssertMsg2( pEffect.IsValid() && pEffect->IsValid(), "%s could not create particle effect %s", C_BaseEntity::Instance( data.m_hEntity )->GetDebugName(), pszName ); if ( pEffect.IsValid() && pEffect->IsValid() ) { if ( (ParticleAttachment_t)data.m_nDamageType == PATTACH_CUSTOMORIGIN ) { pEffect->SetSortOrigin( data.m_vOrigin ); pEffect->SetControlPoint( 0, data.m_vOrigin ); pEffect->SetControlPoint( 1, data.m_vStart ); Vector vecForward, vecRight, vecUp; AngleVectors( data.m_vAngles, &vecForward, &vecRight, &vecUp ); pEffect->SetControlPointOrientation( 0, vecForward, vecRight, vecUp ); } } } } } else { CSmartPtr<CNewParticleEffect> pEffect = CNewParticleEffect::Create( NULL, pszName ); if ( pEffect->IsValid() ) { pEffect->SetSortOrigin( data.m_vOrigin ); pEffect->SetControlPoint( 0, data.m_vOrigin ); pEffect->SetControlPoint( 1, data.m_vStart ); Vector vecForward, vecRight, vecUp; AngleVectors( data.m_vAngles, &vecForward, &vecRight, &vecUp ); pEffect->SetControlPointOrientation( 0, vecForward, vecRight, vecUp ); } } }
//----------------------------------------------------------------------------- // 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 ); } } } } }
float CASW_DamageAllocationMgr::Allocate( IndexType_t iTarget, CBaseEntity *pProjectile, float flDamage ) { if ( !IsValid( iTarget ) ) return 0; Rebuild( iTarget ); AssertMsg2( !IsProjectileForTarget( iTarget, pProjectile ), "Double-allocated %s to %s\n", pProjectile->GetDebugName(), iTarget->m_hTargeted->GetDebugName() ); tuple_t * RESTRICT ptarget = &Elem(iTarget); ProjectilePool_t::IndexLocalType_t projIdx = m_ProjectileLists.Alloc( true ); m_ProjectileLists[projIdx].m_hHandle = pProjectile; m_ProjectileLists[projIdx].m_flDamage = flDamage; if ( m_ProjectileLists.IsValidIndex(ptarget->m_nProjectiles) ) { m_ProjectileLists.LinkBefore( ptarget->m_nProjectiles, projIdx ); } ptarget->m_nProjectiles = projIdx; return ptarget->m_flAccumulatedDamage += flDamage; }
//----------------------------------------------------------------------------- // 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 ); } } } } }
int CPhysicsSurfaceProps::ParseSurfaceData(const char* pFilename, const char* pTextfile) { if (!AddFileToDatabase(pFilename)) return 0; KeyValues* surfprops = new KeyValues(""); surfprops->LoadFromBuffer(pFilename, pTextfile); for (KeyValues* surface = surfprops; surface; surface = surface->GetNextKey()) { CSurface prop; int baseMaterial = GetSurfaceIndex("default"); memset(&prop.data, 0, sizeof(prop.data)); prop.m_name = m_strings->AddString(surface->GetName()); prop.data.game.material = 0; prop.data.game.maxSpeedFactor = 1.0f; prop.data.game.jumpFactor = 1.0f; prop.data.game.climbable = 0.0f; CopyPhysicsProperties(&prop, baseMaterial); for (KeyValues* data = surface->GetFirstSubKey(); data; data = data->GetNextKey()) { const char* key = data->GetName(); if (!strcmpi(key, "base")) { baseMaterial = GetSurfaceIndex(data->GetString()); CopyPhysicsProperties(&prop, baseMaterial); } else if (!strcmpi(key, "thickness")) { prop.data.physics.thickness = data->GetFloat(); } else if (!strcmpi(key, "density")) { prop.data.physics.density = data->GetFloat(); } else if (!strcmpi(key, "elasticity")) { prop.data.physics.elasticity = data->GetFloat(); } else if (!strcmpi(key, "friction")) { prop.data.physics.friction = data->GetFloat(); } else if (!strcmpi(key, "dampening")) { prop.data.physics.dampening = data->GetFloat(); } else if (!strcmpi(key, "audioreflectivity")) { prop.data.audio.reflectivity = data->GetFloat(); } else if (!strcmpi(key, "audiohardnessfactor")) { prop.data.audio.hardnessFactor = data->GetFloat(); } else if (!strcmpi(key, "audioroughnessfactor")) { prop.data.audio.roughnessFactor = data->GetFloat(); } else if (!strcmpi(key, "scrapeRoughThreshold")) { prop.data.audio.roughThreshold = data->GetFloat(); } else if (!strcmpi(key, "impactHardThreshold")) { prop.data.audio.hardThreshold = data->GetFloat(); } else if (!strcmpi(key, "audioHardMinVelocity")) { prop.data.audio.hardVelocityThreshold = data->GetFloat(); } else if (!strcmpi(key, "maxspeedfactor")) { prop.data.game.maxSpeedFactor = data->GetFloat(); } else if (!strcmpi(key, "jumpfactor")) { prop.data.game.jumpFactor = data->GetFloat(); } else if (!strcmpi(key, "climbable")) { prop.data.game.climbable = data->GetInt(); } else if (!strcmpi(key, "gamematerial")) { if (data->GetDataType() == KeyValues::TYPE_STRING && strlen(data->GetString()) == 1) { prop.data.game.material = toupper(data->GetString()[0]); } else { prop.data.game.material = data->GetInt(); } } else if (!strcmpi(key, "stepleft")) { CUtlSymbol sym = m_strings->AddString(data->GetString()); prop.data.sounds.stepleft = m_soundList.AddToTail(sym); } else if (!strcmpi(key, "stepright")) { CUtlSymbol sym = m_strings->AddString(data->GetString()); prop.data.sounds.stepright = m_soundList.AddToTail(sym); } else if (!strcmpi(key, "impactsoft")) { CUtlSymbol sym = m_strings->AddString(data->GetString()); prop.data.sounds.impactSoft = m_soundList.AddToTail(sym); } else if (!strcmpi(key, "impacthard")) { CUtlSymbol sym = m_strings->AddString(data->GetString()); prop.data.sounds.impactHard = m_soundList.AddToTail(sym); } else if (!strcmpi(key, "scrapesmooth")) { CUtlSymbol sym = m_strings->AddString(data->GetString()); prop.data.sounds.scrapeSmooth = m_soundList.AddToTail(sym); } else if (!strcmpi(key, "scraperough")) { CUtlSymbol sym = m_strings->AddString(data->GetString()); prop.data.sounds.scrapeRough = m_soundList.AddToTail(sym); } else if (!strcmpi(key, "bulletimpact")) { CUtlSymbol sym = m_strings->AddString(data->GetString()); prop.data.sounds.bulletImpact = m_soundList.AddToTail(sym); } else if (!strcmpi(key, "break")) { CUtlSymbol sym = m_strings->AddString(data->GetString()); prop.data.sounds.breakSound = m_soundList.AddToTail(sym); } else if (!strcmpi(key, "strain")) { CUtlSymbol sym = m_strings->AddString(data->GetString()); prop.data.sounds.strainSound = m_soundList.AddToTail(sym); } else if (!strcmpi(key, "rolling")) { CUtlSymbol sym = m_strings->AddString(data->GetString()); prop.data.sounds.rolling = m_soundList.AddToTail(sym); } else { AssertMsg2(0, "Bad surfaceprop key %s (%s)\n", key, data->GetString()); } } if (GetSurfaceIndex(m_strings->String(prop.m_name)) >= 0) break; m_props.AddToTail(prop); } surfprops->deleteThis(); return 0; }
// Pick a sequence for the given activity. If the current sequence is appropriate for the // current activity, and its stored weight is negative (whatever that means), always select // it. Otherwise perform a weighted selection -- imagine a large roulette wheel, with each // sequence having a number of spaces corresponding to its weight. int CStudioHdr::CActivityToSequenceMapping::SelectWeightedSequence( CStudioHdr *pstudiohdr, int activity, int curSequence ) { // is the current sequence appropriate? if (curSequence >= 0) { mstudioseqdesc_t &seqdesc = pstudiohdr->pSeqdesc( curSequence ); if (seqdesc.activity == activity && seqdesc.actweight < 0) return curSequence; } if ( !pstudiohdr->SequencesAvailable() ) { return ACTIVITY_NOT_AVAILABLE; } if ( pstudiohdr->GetNumSeq() == 1 ) { AssertMsg( 0, "Expected single sequence case to be handled in ::SelectWeightedSequence()" ); return ACTIVITY_NOT_AVAILABLE; } if (!ValidateAgainst(pstudiohdr)) { AssertMsg1(false, "CStudioHdr %s has changed its vmodel pointer without reinitializing its activity mapping! Now performing emergency reinitialization.", pstudiohdr->pszName()); ExecuteOnce(DebuggerBreakIfDebugging()); Reinitialize(pstudiohdr); } // a null m_pSequenceTuples just means that this studio header has no activities. if (!m_pSequenceTuples) return ACTIVITY_NOT_AVAILABLE; // get the data for the given activity HashValueType dummy( activity, 0, 0, 0 ); UtlHashHandle_t handle = m_ActToSeqHash.Find(dummy); if (!m_ActToSeqHash.IsValidHandle(handle)) { return ACTIVITY_NOT_AVAILABLE; } const HashValueType * __restrict actData = &m_ActToSeqHash[handle]; AssertMsg2( actData->totalWeight > 0, "Activity %s has total weight of %d!", activity, actData->totalWeight ); int weighttotal = actData->totalWeight; // failsafe if the weight is 0: assume the artist screwed up and that the first sequence // for this activity should be returned. int randomValue = 0; if ( actData->totalWeight <= 0 ) { Warning( "Activity %s has %d sequences with a total weight of %d!", ActivityList_NameForIndex(activity), actData->count, actData->totalWeight ); return (m_pSequenceTuples + actData->startingIdx)->seqnum; } else if ( actData->totalWeight == 1 ) { randomValue = 0; } else { // generate a random number from 0 to the total weight if ( IsInPrediction() ) { randomValue = SharedRandomInt( "SelectWeightedSequence", 0, weighttotal - 1 ); } else { randomValue = RandomInt( 0, weighttotal - 1 ); } } // chug through the entries in the list (they are sequential therefore cache-coherent) // until we run out of random juice SequenceTuple * __restrict sequenceInfo = m_pSequenceTuples + actData->startingIdx; const SequenceTuple *const stopHere = sequenceInfo + actData->count; // this is a backup // in case the weights are somehow miscalculated -- we don't read or write through // it (because it aliases the restricted pointer above); it's only here for // the comparison. while (randomValue >= sequenceInfo->weight && sequenceInfo < stopHere) { randomValue -= sequenceInfo->weight; ++sequenceInfo; } return sequenceInfo->seqnum; }