void CSDKPlayer::FireBullet( Vector vecSrc, // shooting postion const QAngle &shootAngles, //shooting angle float vecSpread, // spread vector int iDamage, // base damage int iBulletType, // ammo type CBaseEntity *pevAttacker, // shooter bool bDoEffects, // create impact effect ? float x, // spread x factor float y // spread y factor ) { float fCurrentDamage = iDamage; // damage of the bullet at it's current trajectory float flCurrentDistance = 0.0; //distance that the bullet has traveled so far Vector vecDirShooting, vecRight, vecUp; AngleVectors( shootAngles, &vecDirShooting, &vecRight, &vecUp ); if ( !pevAttacker ) pevAttacker = this; // the default attacker is ourselves // add the spray Vector vecDir = vecDirShooting + x * vecSpread * vecRight + y * vecSpread * vecUp; VectorNormalize( vecDir ); float flMaxRange = 8000; Vector vecEnd = vecSrc + vecDir * flMaxRange; // max bullet range is 10000 units trace_t tr; // main enter bullet trace UTIL_TraceLine( vecSrc, vecEnd, MASK_SOLID|CONTENTS_DEBRIS|CONTENTS_HITBOX, this, COLLISION_GROUP_NONE, &tr ); if ( tr.fraction == 1.0f ) return; // we didn't hit anything, stop tracing shoot if ( sv_showimpacts.GetBool() ) { #ifdef CLIENT_DLL // draw red client impact markers debugoverlay->AddBoxOverlay( tr.endpos, Vector(-2,-2,-2), Vector(2,2,2), QAngle( 0, 0, 0), 255,0,0,127, 4 ); if ( tr.m_pEnt && tr.m_pEnt->IsPlayer() ) { C_BasePlayer *player = ToBasePlayer( tr.m_pEnt ); player->DrawClientHitboxes( 4, true ); } #else // draw blue server impact markers NDebugOverlay::Box( tr.endpos, Vector(-2,-2,-2), Vector(2,2,2), 0,0,255,127, 4 ); if ( tr.m_pEnt && tr.m_pEnt->IsPlayer() ) { CBasePlayer *player = ToBasePlayer( tr.m_pEnt ); player->DrawServerHitboxes( 4, true ); } #endif } //calculate the damage based on the distance the bullet travelled. flCurrentDistance += tr.fraction * flMaxRange; // damage get weaker of distance fCurrentDamage *= pow ( 0.85f, (flCurrentDistance / 500)); int iDamageType = DMG_BULLET | DMG_NEVERGIB; if( bDoEffects ) { // See if the bullet ended up underwater + started out of the water if ( enginetrace->GetPointContents( tr.endpos ) & (CONTENTS_WATER|CONTENTS_SLIME) ) { trace_t waterTrace; UTIL_TraceLine( vecSrc, tr.endpos, (MASK_SHOT|CONTENTS_WATER|CONTENTS_SLIME), this, COLLISION_GROUP_NONE, &waterTrace ); if( waterTrace.allsolid != 1 ) { CEffectData data; data.m_vOrigin = waterTrace.endpos; data.m_vNormal = waterTrace.plane.normal; data.m_flScale = random->RandomFloat( 8, 12 ); if ( waterTrace.contents & CONTENTS_SLIME ) { data.m_fFlags |= FX_WATER_IN_SLIME; } DispatchEffect( "gunshotsplash", data ); } } else { //Do Regular hit effects // Don't decal nodraw surfaces if ( !( tr.surface.flags & (SURF_SKY|SURF_NODRAW|SURF_HINT|SURF_SKIP) ) ) { CBaseEntity *pEntity = tr.m_pEnt; if ( !( !friendlyfire.GetBool() && pEntity && pEntity->IsPlayer() && pEntity->GetTeamNumber() == GetTeamNumber() ) ) { UTIL_ImpactTrace( &tr, iDamageType ); } } } } // bDoEffects // add damage to entity that we hit #ifdef GAME_DLL ClearMultiDamage(); CTakeDamageInfo info( pevAttacker, pevAttacker, fCurrentDamage, iDamageType ); CalculateBulletDamageForce( &info, iBulletType, vecDir, tr.endpos ); tr.m_pEnt->DispatchTraceAttack( info, vecDir, &tr ); TraceAttackToTriggers( info, tr.startpos, tr.endpos, vecDir ); ApplyMultiDamage(); #endif }
void CDODPlayer::FireBullets( const FireBulletsInfo_t &info ) { trace_t tr; trace_t reverseTr; //Used to find exit points static int iMaxPenetrations = 6; int iPenetrations = 0; float flDamage = info.m_iDamage; //Remaining damage in the bullet Vector vecSrc = info.m_vecSrc; Vector vecEnd = vecSrc + info.m_vecDirShooting * info.m_flDistance; static int iTraceMask = ( ( MASK_SOLID | CONTENTS_DEBRIS | CONTENTS_HITBOX | CONTENTS_PRONE_HELPER ) & ~CONTENTS_GRATE ); CBaseEntity *pLastHitEntity = this; // start with us so we don't trace ourselves int iDamageType = GetAmmoDef()->DamageType( info.m_iAmmoType ); int iCollisionGroup = COLLISION_GROUP_NONE; #ifdef GAME_DLL bool iNumHeadshots = 0; #endif while ( flDamage > 0 && iPenetrations < iMaxPenetrations ) { //DevMsg( 2, "penetration: %d, starting dmg: %.1f\n", iPenetrations, flDamage ); CBaseEntity *pPreviousHit = pLastHitEntity; // skip the shooter always CTraceFilterSkipTwoEntities ignoreShooterAndPrevious( this, pPreviousHit, iCollisionGroup ); UTIL_TraceLine( vecSrc, vecEnd, iTraceMask, &ignoreShooterAndPrevious, &tr ); const float rayExtension = 40.0f; UTIL_ClipTraceToPlayers( vecSrc, vecEnd + info.m_vecDirShooting * rayExtension, iTraceMask, &ignoreShooterAndPrevious, &tr ); if ( tr.fraction == 1.0f ) break; // we didn't hit anything, stop tracing shoot // New hitbox code that uses hitbox groups instead of trying to trace // through the player if ( tr.m_pEnt && tr.m_pEnt->IsPlayer() ) { switch( tr.hitgroup ) { #ifdef GAME_DLL case HITGROUP_HEAD: { if ( tr.m_pEnt->GetTeamNumber() != GetTeamNumber() ) { iNumHeadshots++; } } break; #endif case HITGROUP_LEFTARM: case HITGROUP_RIGHTARM: { //DevMsg( 2, "Hit arms, tracing against alt hitboxes.. \n" ); CDODPlayer *pPlayer = ToDODPlayer( tr.m_pEnt ); // set hitbox set to "dod_no_arms" pPlayer->SetHitboxSet( 1 ); trace_t newTr; // re-fire the trace UTIL_TraceLine( vecSrc, vecEnd, iTraceMask, &ignoreShooterAndPrevious, &newTr ); // if we hit the same player in the chest if ( tr.m_pEnt == newTr.m_pEnt ) { //DevMsg( 2, ".. and we hit the chest.\n" ); Assert( tr.hitgroup != newTr.hitgroup ); // If we hit this, hitbox sets are broken // use that damage instead tr = newTr; } // set hitboxes back to "dod" pPlayer->SetHitboxSet( 0 ); } break; default: break; } } pLastHitEntity = tr.m_pEnt; if ( sv_showimpacts.GetBool() ) { #ifdef CLIENT_DLL // draw red client impact markers debugoverlay->AddBoxOverlay( tr.endpos, Vector(-1,-1,-1), Vector(1,1,1), QAngle(0,0,0), 255, 0, 0, 127, 4 ); if ( tr.m_pEnt && tr.m_pEnt->IsPlayer() ) { C_BasePlayer *player = ToBasePlayer( tr.m_pEnt ); player->DrawClientHitboxes( 4, true ); } #else // draw blue server impact markers NDebugOverlay::Box( tr.endpos, Vector(-1,-1,-1), Vector(1,1,1), 0,0,255,127, 4 ); if ( tr.m_pEnt && tr.m_pEnt->IsPlayer() ) { CBasePlayer *player = ToBasePlayer( tr.m_pEnt ); player->DrawServerHitboxes( 4, true ); } #endif } #ifdef CLIENT_DLL // See if the bullet ended up underwater + started out of the water if ( enginetrace->GetPointContents( tr.endpos ) & (CONTENTS_WATER|CONTENTS_SLIME) ) { trace_t waterTrace; UTIL_TraceLine( vecSrc, tr.endpos, (MASK_SHOT|CONTENTS_WATER|CONTENTS_SLIME), this, iCollisionGroup, &waterTrace ); if( waterTrace.allsolid != 1 ) { CEffectData data; data.m_vOrigin = waterTrace.endpos; data.m_vNormal = waterTrace.plane.normal; data.m_flScale = random->RandomFloat( 8, 12 ); if ( waterTrace.contents & CONTENTS_SLIME ) { data.m_fFlags |= FX_WATER_IN_SLIME; } DispatchEffect( "gunshotsplash", data ); } } else { //Do Regular hit effects // Don't decal nodraw surfaces if ( !( tr.surface.flags & (SURF_SKY|SURF_NODRAW|SURF_HINT|SURF_SKIP) ) ) { CBaseEntity *pEntity = tr.m_pEnt; if ( !( !friendlyfire.GetBool() && pEntity && pEntity->GetTeamNumber() == GetTeamNumber() ) ) { UTIL_ImpactTrace( &tr, iDamageType ); } } } #endif // Get surface where the bullet entered ( if it had different surfaces on enter and exit ) surfacedata_t *pSurfaceData = physprops->GetSurfaceData( tr.surface.surfaceProps ); Assert( pSurfaceData ); float flMaterialMod = GetDensityFromMaterial(pSurfaceData); if ( iDamageType & DMG_MACHINEGUN ) { flMaterialMod *= 0.65; } // try to penetrate object Vector penetrationEnd; float flMaxDistance = flDamage / flMaterialMod; #ifndef CLIENT_DLL ClearMultiDamage(); float flActualDamage = flDamage; CTakeDamageInfo dmgInfo( info.m_pAttacker, info.m_pAttacker, flActualDamage, iDamageType ); CalculateBulletDamageForce( &dmgInfo, info.m_iAmmoType, info.m_vecDirShooting, tr.endpos ); tr.m_pEnt->DispatchTraceAttack( dmgInfo, info.m_vecDirShooting, &tr ); DevMsg( 2, "Giving damage ( %.1f ) to entity of type %s\n", flActualDamage, tr.m_pEnt->GetClassname() ); TraceAttackToTriggers( dmgInfo, tr.startpos, tr.endpos, info.m_vecDirShooting ); #endif int stepsize = 16; // displacement always stops the bullet if ( tr.IsDispSurface() ) { DevMsg( 2, "bullet was stopped by displacement\n" ); ApplyMultiDamage(); break; } // trace through the solid to find the exit point and how much material we went through if ( !TraceToExit( tr.endpos, info.m_vecDirShooting, penetrationEnd, stepsize, flMaxDistance ) ) { DevMsg( 2, "bullet was stopped\n" ); ApplyMultiDamage(); break; } // find exact penetration exit CTraceFilterSimple ignoreShooter( this, iCollisionGroup ); UTIL_TraceLine( penetrationEnd, tr.endpos, iTraceMask, &ignoreShooter, &reverseTr ); // Now we can apply the damage, after we have traced the entity // so it doesn't break or die before we have a change to test against it #ifndef CLIENT_DLL ApplyMultiDamage(); #endif // Continue looking for the exit point if( reverseTr.m_pEnt != tr.m_pEnt && reverseTr.m_pEnt != NULL ) { // something was blocking, trace again CTraceFilterSkipTwoEntities ignoreShooterAndBlocker( this, reverseTr.m_pEnt, iCollisionGroup ); UTIL_TraceLine( penetrationEnd, tr.endpos, iTraceMask, &ignoreShooterAndBlocker, &reverseTr ); } if ( sv_showimpacts.GetBool() ) { debugoverlay->AddLineOverlay( penetrationEnd, reverseTr.endpos, 255, 0, 0, true, 3.0 ); } // penetration was successful #ifdef CLIENT_DLL // bullet did penetrate object, exit Decal if ( !( reverseTr.surface.flags & (SURF_SKY|SURF_NODRAW|SURF_HINT|SURF_SKIP) ) ) { CBaseEntity *pEntity = reverseTr.m_pEnt; if ( !( !friendlyfire.GetBool() && pEntity && pEntity->GetTeamNumber() == GetTeamNumber() ) ) { UTIL_ImpactTrace( &reverseTr, iDamageType ); } } #endif //setup new start end parameters for successive trace // New start point is our last exit point vecSrc = reverseTr.endpos + /* 1.0 * */ info.m_vecDirShooting; // Reduce bullet damage by material and distanced travelled through that material // if it is < 0 we won't go through the loop again float flTraceDistance = VectorLength( reverseTr.endpos - tr.endpos ); flDamage -= flMaterialMod * flTraceDistance; if( flDamage > 0 ) { DevMsg( 2, "Completed penetration, new damage is %.1f\n", flDamage ); } else { DevMsg( 2, "bullet was stopped\n" ); } iPenetrations++; } #ifdef GAME_DLL HandleHeadshotAchievement( iNumHeadshots ); #endif }
void CSDKPlayer::FireBullet( Vector vecSrc, // shooting postion const QAngle &shootAngles, //shooting angle float vecSpread, // spread vector SDKWeaponID eWeaponID, // weapon that fired this shot int iDamage, // base damage int iBulletType, // ammo type CBaseEntity *pevAttacker, // shooter bool bDoEffects, // create impact effect ? float x, // spread x factor float y // spread y factor ) { float flCurrentDistance = 0.0; //distance that the bullet has traveled so far Vector vecDirShooting, vecRight, vecUp; AngleVectors( shootAngles, &vecDirShooting, &vecRight, &vecUp ); if ( !pevAttacker ) pevAttacker = this; // the default attacker is ourselves // add the spray Vector vecDir = vecDirShooting + x * vecSpread * vecRight + y * vecSpread * vecUp; VectorNormalize( vecDir ); float flMaxRange = 8000; Vector vecEnd = vecSrc + vecDir * flMaxRange; // max bullet range is 10000 units CBaseEntity* pIgnore = this; // initialize these before the penetration loop, we'll need them to make our tracer after Vector vecTracerSrc = vecSrc; trace_t tr; // main enter bullet trace for (size_t i = 0; i < 5; i++) { CTraceFilterSimpleList tf(COLLISION_GROUP_NONE); tf.AddEntityToIgnore(this); tf.AddEntityToIgnore(pIgnore); UTIL_TraceLine( vecSrc, vecEnd, MASK_SOLID|CONTENTS_DEBRIS|CONTENTS_HITBOX, &tf, &tr ); if ( tr.fraction == 1.0f ) break; // we didn't hit anything, stop tracing shoot if ( sv_showimpacts.GetBool() ) { #ifdef CLIENT_DLL // draw red client impact markers debugoverlay->AddBoxOverlay( tr.endpos, Vector(-2,-2,-2), Vector(2,2,2), QAngle( 0, 0, 0), 255,0,0,127, 4 ); if ( tr.m_pEnt && tr.m_pEnt->IsPlayer() ) { C_BasePlayer *player = ToBasePlayer( tr.m_pEnt ); player->DrawClientHitboxes( 4, true ); } #else // draw blue server impact markers NDebugOverlay::Box( tr.endpos, Vector(-2,-2,-2), Vector(2,2,2), 0,0,255,127, 4 ); if ( tr.m_pEnt && tr.m_pEnt->IsPlayer() ) { CBasePlayer *player = ToBasePlayer( tr.m_pEnt ); player->DrawServerHitboxes( 4, true ); } #endif } weapontype_t eWeaponType = WT_NONE; CSDKWeaponInfo *pWeaponInfo = CSDKWeaponInfo::GetWeaponInfo(eWeaponID); Assert(pWeaponInfo); if (pWeaponInfo) eWeaponType = pWeaponInfo->m_eWeaponType; float flDamageMultiplier = 1; float flMaxRange = 3000; // Power formula works like so: // pow( x, distance/y ) // The damage will be at 1 when the distance is 0 units, and at // x% when the distance is y units, with a gradual decay approaching zero switch (eWeaponType) { case WT_RIFLE: flDamageMultiplier = 0.75f; flMaxRange = 3000; break; case WT_SHOTGUN: flDamageMultiplier = 0.40f; flMaxRange = 500; break; case WT_SMG: flDamageMultiplier = 0.50f; flMaxRange = 1000; break; case WT_PISTOL: default: flDamageMultiplier = 0.55f; flMaxRange = 1500; break; } //calculate the damage based on the distance the bullet travelled. flCurrentDistance += tr.fraction * flMaxRange; // First 500 units, no decrease in damage. if (eWeaponType == WT_SHOTGUN) flCurrentDistance -= 350; else flCurrentDistance -= 500; if (flCurrentDistance < 0) flCurrentDistance = 0; if (flCurrentDistance > flMaxRange) flCurrentDistance = flMaxRange; float flDistanceMultiplier = pow(flDamageMultiplier, (flCurrentDistance / flMaxRange)); int iDamageType = DMG_BULLET | DMG_NEVERGIB | GetAmmoDef()->DamageType(iBulletType); if (i == 0) iDamageType |= DMG_DIRECT; if( bDoEffects ) { // See if the bullet ended up underwater + started out of the water if ( enginetrace->GetPointContents( tr.endpos ) & (CONTENTS_WATER|CONTENTS_SLIME) ) { trace_t waterTrace; UTIL_TraceLine( vecSrc, tr.endpos, (MASK_SHOT|CONTENTS_WATER|CONTENTS_SLIME), pIgnore, COLLISION_GROUP_NONE, &waterTrace ); if( waterTrace.allsolid != 1 ) { CEffectData data; data.m_vOrigin = waterTrace.endpos; data.m_vNormal = waterTrace.plane.normal; data.m_flScale = random->RandomFloat( 8, 12 ); if ( waterTrace.contents & CONTENTS_SLIME ) { data.m_fFlags |= FX_WATER_IN_SLIME; } DispatchEffect( "gunshotsplash", data ); } } else { //Do Regular hit effects // Don't decal nodraw surfaces if ( !( tr.surface.flags & (SURF_SKY|SURF_NODRAW|SURF_HINT|SURF_SKIP) ) ) { CBaseEntity *pEntity = tr.m_pEnt; //Tony; only while using teams do we check for friendly fire. if ( pEntity && pEntity->IsPlayer() && (pEntity->GetBaseAnimating() && !pEntity->GetBaseAnimating()->IsRagdoll()) ) { #if defined ( SDK_USE_TEAMS ) if ( pEntity->GetTeamNumber() == GetTeamNumber() ) { if ( !friendlyfire.GetBool() ) UTIL_ImpactTrace( &tr, iDamageType ); } #else UTIL_ImpactTrace( &tr, iDamageType ); #endif } //Tony; non player, just go nuts, else { UTIL_ImpactTrace( &tr, iDamageType ); } } } } // bDoEffects // add damage to entity that we hit #ifdef GAME_DLL float flBulletDamage = iDamage * flDistanceMultiplier / (i+1); // Each iteration the bullet drops in strength ClearMultiDamage(); CTakeDamageInfo info( pevAttacker, pevAttacker, flBulletDamage, iDamageType ); CalculateBulletDamageForce( &info, iBulletType, vecDir, tr.endpos ); tr.m_pEnt->DispatchTraceAttack( info, vecDir, &tr ); TraceAttackToTriggers( info, tr.startpos, tr.endpos, vecDir ); ApplyMultiDamage(); #else flDistanceMultiplier = flDistanceMultiplier; // Silence warning. #endif pIgnore = tr.m_pEnt; float flPenetrationDistance; switch (eWeaponType) { case WT_RIFLE: flPenetrationDistance = 25; break; case WT_SHOTGUN: flPenetrationDistance = 5; break; case WT_SMG: flPenetrationDistance = 15; break; case WT_PISTOL: default: flPenetrationDistance = 15; break; } Vector vecBackwards = tr.endpos + vecDir * flPenetrationDistance; if (tr.m_pEnt->IsBSPModel()) UTIL_TraceLine( vecBackwards, tr.endpos, CONTENTS_SOLID|CONTENTS_MOVEABLE, NULL, COLLISION_GROUP_NONE, &tr ); else UTIL_TraceLine( vecBackwards, tr.endpos, CONTENTS_HITBOX, NULL, COLLISION_GROUP_NONE, &tr ); if (tr.startsolid) break; if (tr.m_pEnt) { // let's have a bullet exit effect if we penetrated a solid surface if (tr.m_pEnt->IsBSPModel()) UTIL_ImpactTrace( &tr, iDamageType ); // ignore the entity we just hit for the next trace to avoid weird impact behaviors pIgnore = tr.m_pEnt; } // Set up the next trace. vecSrc = tr.endpos + vecDir; // One unit in the direction of fire so that we firmly embed ourselves in whatever solid was hit. } // the bullet's done penetrating, let's spawn our particle system if (bDoEffects && (pevAttacker == this)) MakeTracer( vecTracerSrc, tr, TRACER_TYPE_DEFAULT ); }
void CBulletManager::SimulateBullet(CBullet& oBullet, float dt) { Vector vecOriginal = oBullet.m_vecOrigin; Assert(oBullet.m_hShooter.Get()); if (!oBullet.m_hShooter) return; bool bHasTraveledBefore = false; if (oBullet.m_flDistanceTraveled > 0) bHasTraveledBefore = true; // initialize these before the penetration loop, we'll need them to make our tracer after Vector vecTracerSrc = oBullet.m_vecOrigin; trace_t tr; // main enter bullet trace float flRange = dt; if (flRange < 0) flRange = 8000; bool bFullPenetrationDistance = false; Vector vecEnd = oBullet.m_vecOrigin + oBullet.m_vecDirection * flRange; int i; for (i = oBullet.m_iPenetrations; i < da_bullet_penetrations.GetInt(); i++) { CTraceFilterSimpleList tf(COLLISION_GROUP_NONE); tf.AddEntityToIgnore(oBullet.m_hShooter); for (int j = 0; j < oBullet.m_ahObjectsHit.Count(); j++) tf.AddEntityToIgnore(oBullet.m_ahObjectsHit[j]); UTIL_TraceLine( oBullet.m_vecOrigin, vecEnd, MASK_SOLID|CONTENTS_DEBRIS|CONTENTS_HITBOX, &tf, &tr ); if (da_bullet_debug.GetBool()) { #ifdef CLIENT_DLL DebugDrawLine(oBullet.m_vecOrigin + Vector(0, 0, 1), tr.endpos + Vector(0, 0, 1), 0, 255, 255, true, dt<0?10:0.1); #else DebugDrawLine(oBullet.m_vecOrigin + Vector(0, 0, 1), tr.endpos + Vector(0, 0, 1), 255, 255, 0, true, dt<0?10:0.1); #endif } Vector vecTraceEnd = tr.endpos; bool bBSPModel = tr.DidHitWorld(); if (tr.allsolid) { oBullet.m_flDistanceTraveled += (oBullet.m_vecOrigin - vecEnd).Length(); oBullet.m_vecOrigin = vecEnd; break; // We're inside something. Do nothing. } if ( sv_showimpacts.GetBool() && tr.fraction < 1.0f ) { #ifdef CLIENT_DLL // draw red client impact markers debugoverlay->AddBoxOverlay( tr.endpos, Vector(-2,-2,-2), Vector(2,2,2), QAngle( 0, 0, 0), 255,0,0,127, sv_showimpacts.GetFloat() ); if ( tr.m_pEnt && tr.m_pEnt->IsPlayer() ) { C_BasePlayer *player = ToBasePlayer( tr.m_pEnt ); player->DrawClientHitboxes( sv_showimpacts.GetFloat(), true ); } #else // draw blue server impact markers NDebugOverlay::Box( tr.endpos, Vector(-2,-2,-2), Vector(2,2,2), 0,0,255,127, sv_showimpacts.GetFloat() ); if ( tr.m_pEnt && tr.m_pEnt->IsPlayer() ) { CBasePlayer *player = ToBasePlayer( tr.m_pEnt ); player->DrawServerHitboxes( sv_showimpacts.GetFloat(), true ); } #endif } Assert(oBullet.m_iBulletType > 0); int iDamageType = DMG_BULLET | DMG_NEVERGIB | GetAmmoDef()->DamageType(oBullet.m_iBulletType); if (i == 0) iDamageType |= DMG_DIRECT; if (tr.startsolid) { trace_t tr2; UTIL_TraceLine( tr.endpos - oBullet.m_vecDirection, oBullet.m_vecOrigin, CONTENTS_SOLID|CONTENTS_MOVEABLE, NULL, COLLISION_GROUP_NONE, &tr2 ); // let's have a bullet exit effect if we penetrated a solid surface if (oBullet.m_bDoEffects && tr2.m_pEnt && tr2.m_pEnt->IsBSPModel()) UTIL_ImpactTrace( &tr2, iDamageType ); // ignore the entity we just hit for the next trace to avoid weird impact behaviors oBullet.m_ahObjectsHit.AddToTail(tr2.m_pEnt); } if ( tr.fraction == 1.0f ) { oBullet.m_flDistanceTraveled += (oBullet.m_vecOrigin - vecEnd).Length(); oBullet.m_vecOrigin = vecEnd; break; // we didn't hit anything, stop tracing shoot } weapontype_t eWeaponType = WT_NONE; CSDKWeaponInfo *pWeaponInfo = CSDKWeaponInfo::GetWeaponInfo(oBullet.m_eWeaponID); Assert(pWeaponInfo); if (pWeaponInfo) eWeaponType = pWeaponInfo->m_eWeaponType; float flDamageMultiplier = 1; float flMaxRange = 3000; // Power formula works like so: // pow( x, distance/y ) // The damage will be at 1 when the distance is 0 units, and at // x% when the distance is y units, with a gradual decay approaching zero switch (eWeaponType) { case WT_RIFLE: flDamageMultiplier = 0.75f; flMaxRange = 3000; break; case WT_SHOTGUN: flDamageMultiplier = 0.40f; flMaxRange = 500; break; case WT_SMG: flDamageMultiplier = 0.50f; flMaxRange = 1000; break; case WT_PISTOL: default: flDamageMultiplier = 0.55f; flMaxRange = 1500; break; } flMaxRange *= oBullet.m_hShooter->m_Shared.ModifySkillValue(1, 0.5f, SKILL_MARKSMAN); //calculate the damage based on the distance the bullet travelled. oBullet.m_flDistanceTraveled += tr.fraction * flRange; float flCurrentDistance = oBullet.m_flDistanceTraveled; // First 500 units, no decrease in damage. if (eWeaponType == WT_SHOTGUN) flCurrentDistance -= 350; else flCurrentDistance -= 500; if (flCurrentDistance < 0) flCurrentDistance = 0; if (flCurrentDistance > flMaxRange) flCurrentDistance = flMaxRange; float flDistanceMultiplier = pow(flDamageMultiplier, (flCurrentDistance / flMaxRange)); if( oBullet.m_bDoEffects ) { // See if the bullet ended up underwater + started out of the water if ( enginetrace->GetPointContents( tr.endpos ) & (CONTENTS_WATER|CONTENTS_SLIME) ) { CBaseEntity* pIgnore; if (oBullet.m_ahObjectsHit.Count()) pIgnore = oBullet.m_ahObjectsHit.Tail(); else pIgnore = oBullet.m_hShooter; trace_t waterTrace; UTIL_TraceLine( oBullet.m_vecOrigin, tr.endpos, (MASK_SHOT|CONTENTS_WATER|CONTENTS_SLIME), pIgnore, COLLISION_GROUP_NONE, &waterTrace ); if( waterTrace.allsolid != 1 ) { CEffectData data; data.m_vOrigin = waterTrace.endpos; data.m_vNormal = waterTrace.plane.normal; data.m_flScale = random->RandomFloat( 8, 12 ); if ( waterTrace.contents & CONTENTS_SLIME ) data.m_fFlags |= FX_WATER_IN_SLIME; DispatchEffect( "gunshotsplash", data ); } } else { //Do Regular hit effects // Don't decal nodraw surfaces if ( !( tr.surface.flags & (SURF_SKY|SURF_NODRAW|SURF_HINT|SURF_SKIP) ) ) { CBaseEntity *pEntity = tr.m_pEnt; //Tony; only while using teams do we check for friendly fire. if ( DAGameRules()->IsTeamplay() && pEntity && pEntity->IsPlayer() && (pEntity->GetBaseAnimating() && !pEntity->GetBaseAnimating()->IsRagdoll()) ) { if ( pEntity->GetTeamNumber() != oBullet.m_hShooter->GetTeamNumber() ) UTIL_ImpactTrace( &tr, iDamageType ); } //Tony; non player, just go nuts, else UTIL_ImpactTrace( &tr, iDamageType ); } } } // bDoEffects // add damage to entity that we hit #ifdef GAME_DLL float flBulletDamage = oBullet.m_iBulletDamage * flDistanceMultiplier / (i+1); // Each iteration the bullet drops in strength if (oBullet.m_hShooter->IsStyleSkillActive(SKILL_MARKSMAN)) flBulletDamage = oBullet.m_iBulletDamage * flDistanceMultiplier / (i/2+1); // Each iteration the bullet drops in strength but not nearly as much. ClearMultiDamage(); CTakeDamageInfo info( oBullet.m_hShooter, oBullet.m_hShooter, oBullet.m_hWeapon, flBulletDamage, iDamageType ); CalculateBulletDamageForce( &info, oBullet.m_iBulletType, oBullet.m_vecDirection, tr.endpos ); tr.m_pEnt->DispatchTraceAttack( info, oBullet.m_vecDirection, &tr ); oBullet.m_hShooter->TraceAttackToTriggers( info, tr.startpos, tr.endpos, oBullet.m_vecDirection ); ApplyMultiDamage(); #else flDistanceMultiplier = flDistanceMultiplier; // Silence warning. #endif if (tr.m_pEnt && !FStrEq(tr.m_pEnt->GetClassname(), "worldspawn")) oBullet.m_ahObjectsHit.AddToTail(tr.m_pEnt); float flPenetrationDistance; switch (eWeaponType) { case WT_RIFLE: flPenetrationDistance = 25; break; case WT_SHOTGUN: flPenetrationDistance = 5; break; case WT_SMG: flPenetrationDistance = 15; break; case WT_PISTOL: default: flPenetrationDistance = 15; break; } flPenetrationDistance = oBullet.m_hShooter->m_Shared.ModifySkillValue(flPenetrationDistance, 1, SKILL_MARKSMAN); Vector vecBackwards = tr.endpos + oBullet.m_vecDirection * flPenetrationDistance; if (tr.m_pEnt->IsBSPModel()) UTIL_TraceLine( vecBackwards, tr.endpos, CONTENTS_SOLID|CONTENTS_MOVEABLE, NULL, COLLISION_GROUP_NONE, &tr ); else UTIL_TraceLine( vecBackwards, tr.endpos, CONTENTS_HITBOX, NULL, COLLISION_GROUP_NONE, &tr ); if (tr.startsolid) { bFullPenetrationDistance = true; break; } // Set up the next trace. One unit in the direction of fire so that we firmly embed // ourselves in whatever solid was hit, to make sure we don't hit it again on next trace. if (dt < 0 && bBSPModel) { UTIL_TraceLine( vecTraceEnd + oBullet.m_vecDirection, vecTraceEnd + oBullet.m_vecDirection * flPenetrationDistance, CONTENTS_SOLID|CONTENTS_MOVEABLE, NULL, COLLISION_GROUP_NONE, &tr ); if (tr.startsolid) oBullet.m_vecOrigin = tr.startpos + oBullet.m_vecDirection; else oBullet.m_vecOrigin = vecTraceEnd + oBullet.m_vecDirection; } else oBullet.m_vecOrigin = vecTraceEnd + oBullet.m_vecDirection; } oBullet.m_iPenetrations = i; // the bullet's done penetrating, let's spawn our particle system if (oBullet.m_bDoEffects && dt < 0) oBullet.m_hShooter->MakeTracer( oBullet.m_vecOrigin, tr, TRACER_TYPE_DEFAULT, !bHasTraveledBefore ); #ifdef CLIENT_DLL if (oBullet.m_hRenderHandle != INVALID_CLIENT_RENDER_HANDLE) ClientLeafSystem()->RenderableChanged( oBullet.m_hRenderHandle ); #endif if (bFullPenetrationDistance || oBullet.m_iPenetrations >= da_bullet_penetrations.GetInt()) oBullet.Deactivate(); if (dt < 0) oBullet.Deactivate(); if (!bHasTraveledBefore && oBullet.m_flCurrAlpha == 0 && oBullet.m_flGoalAlpha == 0) oBullet.m_bActive = false; if (da_bullet_debug.GetBool()) { #ifdef CLIENT_DLL DebugDrawLine(vecOriginal, oBullet.m_vecOrigin, 0, 0, 255, true, dt<0?10:0.1); #else DebugDrawLine(vecOriginal, oBullet.m_vecOrigin, 255, 0, 0, true, dt<0?10:0.1); #endif } }