//----------------------------------------------------------------------------- // Purpose: Handle weapon impacts //----------------------------------------------------------------------------- void ImpactCallback( const CEffectData &data ) { VPROF_BUDGET( "ImpactCallback", VPROF_BUDGETGROUP_PARTICLE_RENDERING ); trace_t tr; Vector vecOrigin, vecStart, vecShotDir; int iMaterial, iDamageType, iHitbox; short nSurfaceProp; C_BaseEntity *pEntity = ParseImpactData( data, &vecOrigin, &vecStart, &vecShotDir, nSurfaceProp, iMaterial, iDamageType, iHitbox ); if ( !pEntity ) { // This happens for impacts that occur on an object that's then destroyed. // Clear out the fraction so it uses the server's data tr.fraction = 1.0; PlayImpactSound( pEntity, tr, vecOrigin, nSurfaceProp ); return; } // If we hit, perform our custom effects and play the sound if ( Impact( vecOrigin, vecStart, iMaterial, iDamageType, iHitbox, pEntity, tr ) ) { // Check for custom effects based on the Decal index PerformCustomEffects( vecOrigin, tr, vecShotDir, iMaterial, 1.0 ); } PlayImpactSound( pEntity, tr, vecOrigin, nSurfaceProp ); }
//----------------------------------------------------------------------------- // Purpose: Handle weapon impacts //----------------------------------------------------------------------------- void ImpactCallback( const CEffectData &data ) { trace_t tr; Vector vecOrigin, vecStart, vecShotDir; int iMaterial, iDamageType, iHitbox; short nSurfaceProp; C_BaseEntity *pEntity = ParseImpactData( data, &vecOrigin, &vecStart, &vecShotDir, nSurfaceProp, iMaterial, iDamageType, iHitbox ); if ( !pEntity ) { // This happens for impacts that occur on an object that's then destroyed. // Clear out the fraction so it uses the server's data tr.fraction = 1.0; PlayImpactSound( pEntity, tr, vecOrigin, nSurfaceProp ); return; } // If we hit, perform our custom effects and play the sound if ( Impact( vecOrigin, vecStart, iMaterial, iDamageType, iHitbox, pEntity, tr ) ) { // Check for custom effects based on the Decal index PerformCustomEffects( vecOrigin, tr, vecShotDir, iMaterial, 1.0 ); //Play a ricochet sound some of the time if( random->RandomInt(1,10) <= 3 && (iDamageType == DMG_BULLET) ) { CLocalPlayerFilter filter; C_BaseEntity::EmitSound( filter, SOUND_FROM_WORLD, "Bounce.Shrapnel", &vecOrigin ); } } PlayImpactSound( pEntity, tr, vecOrigin, nSurfaceProp ); }
//----------------------------------------------------------------------------- // Purpose: Handle weapon impacts from the helicopter shooting (cheaper versions) //----------------------------------------------------------------------------- void ImpactHelicopterCallback( const CEffectData &data ) { VPROF_BUDGET( "ImpactHelicopterCallback", VPROF_BUDGETGROUP_PARTICLE_RENDERING ); trace_t tr; Vector vecOrigin, vecStart, vecShotDir; int iMaterial, iDamageType, iHitbox; short nSurfaceProp; C_BaseEntity *pEntity = ParseImpactData( data, &vecOrigin, &vecStart, &vecShotDir, nSurfaceProp, iMaterial, iDamageType, iHitbox ); if ( !pEntity ) { // This happens for impacts that occur on an object that's then destroyed. // Clear out the fraction so it uses the server's data tr.fraction = 1.0; PlayImpactSound( pEntity, tr, vecOrigin, nSurfaceProp ); return; } // If we hit, perform our custom effects and play the sound. Don't create decals if ( Impact( vecOrigin, vecStart, iMaterial, iDamageType, iHitbox, pEntity, tr, IMPACT_NODECAL | IMPACT_REPORT_RAGDOLL_IMPACTS ) ) { FX_AirboatGunImpact( vecOrigin, tr.plane.normal, IsXbox() ? 1 : 2 ); // Only do metal + computer custom effects if ( (iMaterial == CHAR_TEX_METAL) || (iMaterial == CHAR_TEX_COMPUTER) ) { PerformCustomEffects( vecOrigin, tr, vecShotDir, iMaterial, 1.0, FLAGS_CUSTIOM_EFFECTS_NOFLECKS ); } } PlayImpactSound( pEntity, tr, vecOrigin, nSurfaceProp ); }
//----------------------------------------------------------------------------- // Purpose: Handle weapon impacts from the airboat gun shooting (cheaper versions) //----------------------------------------------------------------------------- void ImpactAirboatGunCallback( const CEffectData &data ) { VPROF_BUDGET( "ImpactAirboatGunCallback", VPROF_BUDGETGROUP_PARTICLE_RENDERING ); trace_t tr; Vector vecOrigin, vecStart, vecShotDir; int iMaterial, iDamageType, iHitbox; short nSurfaceProp; C_BaseEntity *pEntity = ParseImpactData( data, &vecOrigin, &vecStart, &vecShotDir, nSurfaceProp, iMaterial, iDamageType, iHitbox ); if ( !pEntity ) { // This happens for impacts that occur on an object that's then destroyed. // Clear out the fraction so it uses the server's data tr.fraction = 1.0; PlayImpactSound( pEntity, tr, vecOrigin, nSurfaceProp ); return; } #if !defined( _XBOX ) // If we hit, perform our custom effects and play the sound. Don't create decals if ( Impact( vecOrigin, vecStart, iMaterial, iDamageType, iHitbox, pEntity, tr, IMPACT_NODECAL | IMPACT_REPORT_RAGDOLL_IMPACTS ) ) { FX_AirboatGunImpact( vecOrigin, tr.plane.normal, 2 ); } #else FX_AirboatGunImpact( vecOrigin, tr.plane.normal, 1 ); #endif }
END_CLASS int hhProjectileStickyCrawlerGrenade::ProcessCollision( const trace_t* collision, const idVec3& velocity ) { idEntity* entHit = gameLocal.entities[ collision->c.entityNum ]; //SAFE_REMOVE( fxFly ); FreeLightDef(); CancelEvents( &EV_Fizzle ); //physicsObj.SetContents( 0 ); physicsObj.PutToRest(); surfTypes_t matterType = gameLocal.GetMatterType( entHit, collision->c.material, "hhProjectile::ProcessCollision" ); return PlayImpactSound( gameLocal.FindEntityDefDict(spawnArgs.GetString("def_damage")), collision->endpos, matterType ); }
//----------------------------------------------------------------------------- // Purpose: Handle weapon impacts //----------------------------------------------------------------------------- void ImpactCallback( const CEffectData &data ) { trace_t tr; Vector vecOrigin, vecStart, vecShotDir; int iMaterial, iDamageType, iHitbox, iEntIndex; ParseImpactData( data, &vecOrigin, &vecStart, &vecShotDir, &iMaterial, &iDamageType, &iHitbox, &iEntIndex ); // If we hit, perform our custom effects and play the sound if ( Impact( vecOrigin, vecStart, iMaterial, iDamageType, iHitbox, iEntIndex, tr ) ) { // Check for custom effects based on the Decal index PerformCustomEffects( vecOrigin, tr, vecShotDir, iMaterial, 1.0 ); PlayImpactSound( tr ); } }
int hhProjectileFreezer::ProcessCollision( const trace_t* collision, const idVec3& velocity ) { idEntity* entHit = gameLocal.entities[ collision->c.entityNum ]; SetOrigin( collision->endpos ); SetAxis( collision->endAxis ); if (entHit) { //rww - may be null on client. DamageEntityHit( collision, velocity, entHit ); } fl.takedamage = false; physicsObj.SetContents( 0 ); physicsObj.PutToRest(); surfTypes_t matterType = gameLocal.GetMatterType( entHit, collision->c.material, "hhProjectile::ProcessCollision" ); return PlayImpactSound( gameLocal.FindEntityDefDict(spawnArgs.GetString("def_damage")), collision->endpos, matterType ); }
//----------------------------------------------------------------------------- // Purpose: Handle gunship impacts //----------------------------------------------------------------------------- void ImpactGunshipCallback( const CEffectData &data ) { trace_t tr; Vector vecOrigin, vecStart, vecShotDir; int iMaterial, iDamageType, iHitbox; short nSurfaceProp; C_BaseEntity *pEntity = ParseImpactData( data, &vecOrigin, &vecStart, &vecShotDir, nSurfaceProp, iMaterial, iDamageType, iHitbox ); if ( !pEntity ) return; // If we hit, perform our custom effects and play the sound if ( Impact( vecOrigin, vecStart, iMaterial, iDamageType, iHitbox, pEntity, tr ) ) { // Check for custom effects based on the Decal index PerformCustomEffects( vecOrigin, tr, vecShotDir, iMaterial, 3 ); } PlayImpactSound( pEntity, tr, vecOrigin, nSurfaceProp ); }
LTBOOL CWeaponFX::CreateObject(ILTClient* pClientDE) { if (!CSpecialFX::CreateObject(pClientDE) || !g_pWeaponMgr) return LTFALSE; CGameSettings* pSettings = g_pInterfaceMgr->GetSettings(); if (!pSettings) return LTFALSE; // Set up our data members... // Set the local client id... uint32 dwId; g_pLTClient->GetLocalClientID(&dwId); m_nLocalId = (uint8)dwId; m_nDetailLevel = pSettings->SpecialFXSetting(); // Fire pos may get tweaked a little... m_vFirePos = CalcFirePos(m_vFirePos); m_vDir = m_vPos - m_vFirePos; m_fFireDistance = m_vDir.Mag(); m_vDir.Norm(); g_pLTClient->AlignRotation(&m_rSurfaceRot, &m_vSurfaceNormal, LTNULL); g_pLTClient->AlignRotation(&m_rDirRot, &m_vDir, LTNULL); SetupExitInfo(); // Calculate if the camera can see the fire position and the impact // position... g_bCanSeeImpactPos = LTTRUE; g_bCanSeeFirePos = LTTRUE; g_bDistantImpactPos = LTFALSE; g_bDistantFirePos = LTFALSE; if (g_vtWeaponFXUseFOVPerformance.GetFloat()) { HOBJECT hCamera = g_pGameClientShell->GetCamera(); LTVector vCameraPos, vU, vR, vF, vDir; LTRotation rCameraRot; g_pLTClient->GetObjectPos(hCamera, &vCameraPos); g_pLTClient->GetObjectRotation(hCamera, &rCameraRot); g_pLTClient->GetRotationVectors(&rCameraRot, &vU, &vR, &vF); vDir = m_vPos - vCameraPos; LTFLOAT fImpactDist = vDir.Mag(); if (fImpactDist > g_vtWeaponFXMaxImpactDist.GetFloat()) { g_bDistantImpactPos = LTTRUE; } vDir.Norm(); LTFLOAT fMul = VEC_DOT(vDir, vF); g_bCanSeeImpactPos = (fMul < g_vtWeaponFXMinImpactDot.GetFloat() ? LTFALSE : LTTRUE); // In multiplayer we need to account for impacts that occur around // our camera that we didn't cause (this is also an issue in single // player, but due to the singler player gameplay dynamics it isn't // as noticeable)... if (!g_bCanSeeImpactPos && IsMultiplayerGame()) { // Somebody else shot this...if the impact is close enough, we // "saw" it... if (m_nLocalId != m_nShooterId && fImpactDist <= g_vtWeaponFXMaxMultiImpactDist.GetFloat()) { g_bCanSeeImpactPos = LTTRUE; } } vDir = m_vFirePos - vCameraPos; if (vDir.Mag() > g_vtWeaponFXMaxFireDist.GetFloat()) { g_bDistantFirePos = LTTRUE; } vDir.Norm(); fMul = VEC_DOT(vDir, vF); g_bCanSeeFirePos = (fMul < g_vtWeaponFXMinFireDot.GetFloat() ? LTFALSE : LTTRUE); } // Determine what container the sfx is in... HLOCALOBJ objList[1]; LTVector vTestPos = m_vPos + m_vSurfaceNormal; // Test a little closer... uint32 dwNum = g_pLTClient->GetPointContainers(&vTestPos, objList, 1); if (dwNum > 0 && objList[0]) { uint32 dwUserFlags; g_pLTClient->GetObjectUserFlags(objList[0], &dwUserFlags); if (dwUserFlags & USRFLG_VISIBLE) { uint16 dwCode; if (g_pLTClient->GetContainerCode(objList[0], &dwCode)) { m_eCode = (ContainerCode)dwCode; } } } // Determine if the fire point is in liquid vTestPos = m_vFirePos + m_vDir; // Test a little further in... dwNum = g_pLTClient->GetPointContainers(&vTestPos, objList, 1); if (dwNum > 0 && objList[0]) { uint32 dwUserFlags; g_pLTClient->GetObjectUserFlags(objList[0], &dwUserFlags); if (dwUserFlags & USRFLG_VISIBLE) { uint16 dwCode; if (g_pLTClient->GetContainerCode(objList[0], &dwCode)) { m_eFirePosCode = (ContainerCode)dwCode; } } } if (IsLiquid(m_eCode)) { m_wImpactFX = m_pAmmo->pUWImpactFX ? m_pAmmo->pUWImpactFX->nFlags : 0; } else { m_wImpactFX = m_pAmmo->pImpactFX ? m_pAmmo->pImpactFX->nFlags : 0; } m_wFireFX = m_pAmmo->pFireFX ? m_pAmmo->pFireFX->nFlags : 0; // Assume alt-fire, silenced, and tracer...these will be cleared by // IgnoreFX if not used... m_wFireFX |= WFX_ALTFIRESND | WFX_SILENCED | WFX_TRACER; // Assume impact ding, it will be cleared if not used... m_wImpactFX |= WFX_IMPACTDING; // Clear all the fire fx we want to ignore... m_wFireFX &= ~m_wIgnoreFX; m_wImpactFX &= ~m_wIgnoreFX; // See if this is a redundant weapon fx (i.e., this client shot the // weapon so they've already seen this fx)... if (g_pGameClientShell->IsMultiplayerGame()) { if (m_pAmmo->eType != PROJECTILE) { if (!m_bLocal && m_nLocalId >= 0 && m_nLocalId == m_nShooterId) { if (m_wImpactFX & WFX_IMPACTDING) { if (g_vtMultiDing.GetFloat()) { PlayImpactDing(); } } return LTFALSE; } } } // Show the fire path...(debugging...) if (g_cvarShowFirePath.GetFloat() > 0) { PLFXCREATESTRUCT pls; pls.vStartPos = m_vFirePos; pls.vEndPos = m_vPos; pls.vInnerColorStart = LTVector(GetRandom(127.0f, 255.0f), GetRandom(127.0f, 255.0f), GetRandom(127.0f, 255.0f)); pls.vInnerColorEnd = pls.vInnerColorStart; pls.vOuterColorStart = LTVector(0, 0, 0); pls.vOuterColorEnd = LTVector(0, 0, 0); pls.fAlphaStart = 1.0f; pls.fAlphaEnd = 1.0f; pls.fMinWidth = 0; pls.fMaxWidth = 10; pls.fMinDistMult = 1.0f; pls.fMaxDistMult = 1.0f; pls.fLifeTime = 10.0f; pls.fAlphaLifeTime = 10.0f; pls.fPerturb = 0.0f; pls.bAdditive = LTFALSE; pls.nWidthStyle = PLWS_CONSTANT; pls.nNumSegments = 2; CSpecialFX* pFX = g_pGameClientShell->GetSFXMgr()->CreateSFX(SFX_POLYLINE_ID, &pls); if (pFX) pFX->Update(); } // If the surface is the sky, don't create any impact related fx... if (m_eSurfaceType != ST_SKY || (m_wImpactFX & WFX_IMPACTONSKY)) { CreateWeaponSpecificFX(); if (g_bCanSeeImpactPos) { if ((m_wImpactFX & WFX_MARK) && ShowsMark(m_eSurfaceType) && (LTBOOL)GetConsoleInt("MarkShow", 1)) { LTBOOL bCreateMark = LTTRUE; if (g_bDistantImpactPos && m_nLocalId == m_nShooterId) { // Assume we'll see the mark if we're zoomed in ;) bCreateMark = g_pGameClientShell->IsZoomed(); } if (bCreateMark) { CreateMark(m_vPos, m_vSurfaceNormal, m_rSurfaceRot, m_eSurfaceType); } } CreateSurfaceSpecificFX(); } PlayImpactSound(); } if (IsBulletTrailWeapon()) { if (IsLiquid(m_eFirePosCode)) { if (m_nDetailLevel != RS_LOW) { CreateBulletTrail(&m_vFirePos); } } } // No tracers under water... if ((LTBOOL)GetConsoleInt("Tracers", 1) && (m_wFireFX & WFX_TRACER) && !IsLiquid(m_eCode)) { CreateTracer(); } if (g_bCanSeeFirePos) { // Only do muzzle fx for the client (not for AIs)... if ((m_wFireFX & WFX_MUZZLE) && (m_nLocalId == m_nShooterId)) { CreateMuzzleFX(); } if (!g_bDistantFirePos && (LTBOOL)GetConsoleInt("ShellCasings", 1) && (m_wFireFX & WFX_SHELL)) { CreateShell(); } if ((m_wFireFX & WFX_LIGHT)) { CreateMuzzleLight(); } } if ((m_wFireFX & WFX_FIRESOUND) || (m_wFireFX & WFX_ALTFIRESND) || (m_wFireFX & WFX_SILENCED)) { PlayFireSound(); } // Only do fly-by sounds for weapons that leave bullet trails... if (IsBulletTrailWeapon()) { PlayBulletFlyBySound(); } return LTFALSE; // Just delete me, I'm done :) }
//Called from PhysicsSimulate() or ReceiveMessage() bool CDHLProjectile::OnTouch( trace_t &touchtr, bool bDecalOnly /*= false*/, ITraceFilter* pTraceFilter /*= NULL*/ ) { //Direction Vector vecDir = touchtr.endpos - touchtr.startpos; if ( vecDir == vec3_origin ) //Sometimes endpos==startpos so we need to get dir from velocity instead { #ifdef CLIENT_DLL vecDir = GetLocalVelocity(); #else vecDir = m_vecCurVelocity; #endif VectorNormalize( vecDir ); } CBaseEntity* ent = touchtr.m_pEnt; if ( !ent ) return false; if ( touchtr.DidHit() ) { //Never collide with self, shooter, or other projectiles if ( ent == this || dynamic_cast<CDHLProjectile*>(ent) || ent == (CBaseEntity*)m_pShooter ) //|| ( (m_iType == DHL_PROJECTILE_TYPE_COMBATKNIFE) && (ent == m_pFiringWeapon) ) ) //Combat knife - don't collide with weapon ent return false; //Hack: Sometimes hits are registered prematurely (usually to the torso area) with no hitbox. Pretend nothing happened unless one is found. if ( ent->IsPlayer() && touchtr.hitgroup == 0 ) return false; //Check friendly fire if ( CheckFriendlyFire( ent ) ) { if ( !bDecalOnly ) { ClearMultiDamage(); //Do damage CTakeDamageInfo dmgInfo( this, GetOwnerEntity(), m_iDamage, DMG_BULLET ); if ( m_iType == DHL_PROJECTILE_TYPE_COMBATKNIFE ) { //CalculateMeleeDamageForce( &dmgInfo, vecDir, touchtr.endpos, 0.01f ); Vector vecForce = vecDir; VectorNormalize( vecForce ); //vecForce *= 10.0f; //Ripped from C_ClientRagdoll::ImpactTrace dmgInfo.SetDamageForce( vecForce ); #ifndef CLIENT_DLL if ( IsOnFire() ) { CBaseAnimating* pBAnim = dynamic_cast<CBaseAnimating*>(ent); if ( pBAnim ) pBAnim->Ignite( 10.0f, false ); } #endif } else CalculateBulletDamageForce( &dmgInfo, m_iAmmoType, vecDir, touchtr.endpos, 1.0f ); dmgInfo.SetDamagePosition( touchtr.endpos ); ent->DispatchTraceAttack( dmgInfo, vecDir, &touchtr ); ApplyMultiDamage(); } #ifdef CLIENT_DLL if ( ent->GetCollisionGroup() == COLLISION_GROUP_BREAKABLE_GLASS ) return false; //Decals and such if ( !( touchtr.surface.flags & SURF_SKY ) && !touchtr.allsolid ) { IPredictionSystem::SuppressEvents( false ); if ( (m_iType == DHL_PROJECTILE_TYPE_BULLET || m_iType == DHL_PROJECTILE_TYPE_PELLET) ) { UTIL_ImpactTrace( &touchtr, DMG_BULLET ); } if ( m_iType == DHL_PROJECTILE_TYPE_COMBATKNIFE ) PlayImpactSound( touchtr.m_pEnt, touchtr, touchtr.endpos, touchtr.surface.surfaceProps ); IPredictionSystem::SuppressEvents( !prediction->IsFirstTimePredicted() ); } #endif } if ( pTraceFilter && m_iType != DHL_PROJECTILE_TYPE_COMBATKNIFE ) { PenetrationData_t nPenetrationData = DHLShared::TestPenetration( touchtr, m_pShooter, pTraceFilter, m_iTimesPenetrated, m_flDistanceTravelled, m_iAmmoType ); if ( nPenetrationData.m_bShouldPenetrate ) { m_flDistanceTravelled += GetLocalOrigin().DistTo( nPenetrationData.m_vecNewBulletPos ); MoveProjectileToPosition( nPenetrationData.m_vecNewBulletPos ); m_iTimesPenetrated++; return true; //Keep going - but don't do anything else in this frame of PhysicsSimulate() } } //We're done unless what we hit was breakable glass if ( ent->GetCollisionGroup() != COLLISION_GROUP_BREAKABLE_GLASS ) { #ifdef CLIENT_DLL m_bCollided = true; AddEffects( EF_NODRAW ); if ( m_pTrail ) //NULL pointer here sometimes somehow... m_pTrail->AddEffects( EF_NODRAW ); #else EntityMessageBegin( this ); WRITE_BYTE( MSG_NOTIFY_REMOVAL ); MessageEnd(); if ( touchtr.DidHitWorld() && m_iType == DHL_PROJECTILE_TYPE_COMBATKNIFE && !( touchtr.surface.flags & SURF_SKY ) ) { CBaseCombatWeapon* pKnifeEnt = assert_cast<CBaseCombatWeapon*>(CreateEntityByName( "weapon_combatknife" )); if ( pKnifeEnt ) { pKnifeEnt->AddSpawnFlags( SF_NORESPAWN ); //Needed for weapon spawn & VPhysics setup to work correctly pKnifeEnt->SetAbsOrigin( touchtr.endpos ); QAngle angles = vec3_angle; Vector vecKnifeDir = touchtr.startpos - touchtr.endpos; VectorAngles( vecKnifeDir, angles ); angles[PITCH] -= 15.0f; //Correct for the .mdl being offset a bit pKnifeEnt->SetLocalAngles( angles ); DispatchSpawn( pKnifeEnt ); //Spawns vphys object and sets it up, essentially a copy of CWeaponHL2MPBase::FallInit() pKnifeEnt->VPhysicsDestroyObject(); //Using SOLID_VPHYSICS instead of SOLID_BBOX (as ordinary weapons do) helps resolve some of the client side collision oddities Assert( pKnifeEnt->VPhysicsInitNormal( SOLID_VPHYSICS, FSOLID_NOT_STANDABLE | FSOLID_TRIGGER, true ) ); pKnifeEnt->SetPickupTouch(); //Sets up automagic removal after time IPhysicsObject* pKnifePhys = pKnifeEnt->VPhysicsGetObject(); if ( pKnifePhys ) { //Knives are solid to bullets...the only way to make them non-solid to bullets is to do SetSolid( SOLID_NONE ) or AddSolidFlags( FSOLID_NOT_SOLID ) //which breaks the +use pickup even with FSOLID_TRIGGER set. Let's just call it a feature :) pKnifePhys->EnableMotion( false ); pKnifePhys->EnableCollisions( false ); } if ( IsOnFire() ) pKnifeEnt->Ignite( 10.0f, false ); } } //SetThink( &CDHLProjectile::SUB_Remove ); //SetNextThink( gpGlobals->curtime + 0.1 ); //SUB_Remove(); //SetMoveType( MOVETYPE_NONE ); m_flRemoveAt = gpGlobals->curtime + 0.1f; //Give the notification message a head start so that the client will have time to react #endif } } return true; }