2020-04-22 18:56:21 +02:00
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
# include "cbase.h"
# include "weapon_csbase.h"
# include "decals.h"
# include "cs_gamerules.h"
# include "weapon_c4.h"
# include "in_buttons.h"
# include "datacache/imdlcache.h"
# ifdef CLIENT_DLL
# include "c_cs_player.h"
# else
# include "cs_player.h"
# include "soundent.h"
# include "bot/cs_bot.h"
# include "KeyValues.h"
# include "triggers.h"
# include "cs_gamestats.h"
# endif
# include "cs_playeranimstate.h"
# include "basecombatweapon_shared.h"
# include "util_shared.h"
# include "takedamageinfo.h"
# include "effect_dispatch_data.h"
# include "engine/ivdebugoverlay.h"
# include "obstacle_pushaway.h"
# include "props_shared.h"
ConVar sv_showimpacts ( " sv_showimpacts " , " 0 " , FCVAR_REPLICATED , " Shows client (red) and server ( blue ) bullet impact point ( 1 = both , 2 = client - only , 3 = server - only ) " ) ;
ConVar sv_showplayerhitboxes ( " sv_showplayerhitboxes " , " 0 " , FCVAR_REPLICATED , " Show lag compensated hitboxes for the specified player index whenever a player fires. " ) ;
2024-01-23 14:32:02 +01:00
ConVar weapon_accuracy_nospread ( " weapon_accuracy_nospread " , " 0 " , FCVAR_REPLICATED ) ;
2020-04-22 18:56:21 +02:00
# define CS_MASK_SHOOT (MASK_SOLID|CONTENTS_DEBRIS)
void DispatchEffect ( const char * pName , const CEffectData & data ) ;
# ifdef _DEBUG
// This is some extra code to collect weapon accuracy stats:
struct bulletdata_s
{
float timedelta ; // time delta since first shot of this round
float derivation ; // derivation for first shoot view angle
int count ;
} ;
# define STATS_MAX_BULLETS 50
static bulletdata_s s_bullet_stats [ STATS_MAX_BULLETS ] ;
Vector s_firstImpact = Vector ( 0 , 0 , 0 ) ;
float s_firstTime = 0 ;
float s_LastTime = 0 ;
int s_bulletCount = 0 ;
void ResetBulletStats ( )
{
s_firstTime = 0 ;
s_LastTime = 0 ;
s_bulletCount = 0 ;
s_firstImpact = Vector ( 0 , 0 , 0 ) ;
Q_memset ( s_bullet_stats , 0 , sizeof ( s_bullet_stats ) ) ;
}
void PrintBulletStats ( )
{
for ( int i = 0 ; i < STATS_MAX_BULLETS ; i + + )
{
if ( s_bullet_stats [ i ] . count = = 0 )
break ;
Msg ( " %3i;%3i;%.4f;%.4f \n " , i , s_bullet_stats [ i ] . count ,
s_bullet_stats [ i ] . timedelta , s_bullet_stats [ i ] . derivation ) ;
}
}
void AddBulletStat ( float time , float dist , Vector & impact )
{
if ( time > s_LastTime + 2.0f )
{
// time delta since last shoot is bigger than 2 seconds, start new row
s_LastTime = s_firstTime = time ;
s_bulletCount = 0 ;
s_firstImpact = impact ;
}
else
{
s_LastTime = time ;
s_bulletCount + + ;
}
if ( s_bulletCount > = STATS_MAX_BULLETS )
s_bulletCount = STATS_MAX_BULLETS - 1 ;
if ( dist < 1 )
dist = 1 ;
int i = s_bulletCount ;
float offset = VectorLength ( s_firstImpact - impact ) ;
float timedelta = time - s_firstTime ;
float derivation = offset / dist ;
float weight = ( float ) s_bullet_stats [ i ] . count / ( float ) ( s_bullet_stats [ i ] . count + 1 ) ;
s_bullet_stats [ i ] . timedelta * = weight ;
s_bullet_stats [ i ] . timedelta + = ( 1.0f - weight ) * timedelta ;
s_bullet_stats [ i ] . derivation * = weight ;
s_bullet_stats [ i ] . derivation + = ( 1.0f - weight ) * derivation ;
s_bullet_stats [ i ] . count + + ;
}
CON_COMMAND ( stats_bullets_reset , " Reset bullet stats " )
{
ResetBulletStats ( ) ;
}
CON_COMMAND ( stats_bullets_print , " Print bullet stats " )
{
PrintBulletStats ( ) ;
}
# endif
float CCSPlayer : : GetPlayerMaxSpeed ( )
{
if ( GetMoveType ( ) = = MOVETYPE_NONE )
{
return CS_PLAYER_SPEED_STOPPED ;
}
if ( IsObserver ( ) )
{
// Player gets speed bonus in observer mode
return CS_PLAYER_SPEED_OBSERVER ;
}
bool bValidMoveState = ( State_Get ( ) = = STATE_ACTIVE | | State_Get ( ) = = STATE_OBSERVER_MODE ) ;
if ( ! bValidMoveState | | m_bIsDefusing | | CSGameRules ( ) - > IsFreezePeriod ( ) )
{
// Player should not move during the freeze period
return CS_PLAYER_SPEED_STOPPED ;
}
float speed = BaseClass : : GetPlayerMaxSpeed ( ) ;
if ( IsVIP ( ) = = true ) // VIP is slow due to the armour he's wearing
{
speed = MIN ( speed , CS_PLAYER_SPEED_VIP ) ;
}
else
{
CWeaponCSBase * pWeapon = dynamic_cast < CWeaponCSBase * > ( GetActiveWeapon ( ) ) ;
if ( pWeapon )
{
if ( HasShield ( ) & & IsShieldDrawn ( ) )
{
speed = MIN ( speed , CS_PLAYER_SPEED_SHIELD ) ;
}
else
{
speed = MIN ( speed , pWeapon - > GetMaxSpeed ( ) ) ;
}
}
}
return speed ;
}
void CCSPlayer : : GetBulletTypeParameters (
int iBulletType ,
float & fPenetrationPower ,
float & flPenetrationDistance )
{
//MIKETODO: make ammo types come from a script file.
if ( IsAmmoType ( iBulletType , BULLET_PLAYER_50AE ) )
{
fPenetrationPower = 30 ;
flPenetrationDistance = 1000.0 ;
}
else if ( IsAmmoType ( iBulletType , BULLET_PLAYER_762MM ) )
{
fPenetrationPower = 39 ;
flPenetrationDistance = 5000.0 ;
}
else if ( IsAmmoType ( iBulletType , BULLET_PLAYER_556MM ) | |
IsAmmoType ( iBulletType , BULLET_PLAYER_556MM_BOX ) )
{
fPenetrationPower = 35 ;
flPenetrationDistance = 4000.0 ;
}
else if ( IsAmmoType ( iBulletType , BULLET_PLAYER_338MAG ) )
{
fPenetrationPower = 45 ;
flPenetrationDistance = 8000.0 ;
}
else if ( IsAmmoType ( iBulletType , BULLET_PLAYER_9MM ) )
{
fPenetrationPower = 21 ;
flPenetrationDistance = 800.0 ;
}
else if ( IsAmmoType ( iBulletType , BULLET_PLAYER_BUCKSHOT ) )
{
fPenetrationPower = 0 ;
flPenetrationDistance = 0.0 ;
}
else if ( IsAmmoType ( iBulletType , BULLET_PLAYER_45ACP ) )
{
fPenetrationPower = 15 ;
flPenetrationDistance = 500.0 ;
}
else if ( IsAmmoType ( iBulletType , BULLET_PLAYER_357SIG ) )
{
fPenetrationPower = 25 ;
flPenetrationDistance = 800.0 ;
}
else if ( IsAmmoType ( iBulletType , BULLET_PLAYER_57MM ) )
{
fPenetrationPower = 30 ;
flPenetrationDistance = 2000.0 ;
}
else
{
// What kind of ammo is this?
Assert ( false ) ;
fPenetrationPower = 0 ;
flPenetrationDistance = 0.0 ;
}
}
static void GetMaterialParameters ( int iMaterial , float & flPenetrationModifier , float & flDamageModifier )
{
switch ( iMaterial )
{
case CHAR_TEX_METAL :
flPenetrationModifier = 0.5 ; // If we hit metal, reduce the thickness of the brush we can't penetrate
flDamageModifier = 0.3 ;
break ;
case CHAR_TEX_DIRT :
flPenetrationModifier = 0.5 ;
flDamageModifier = 0.3 ;
break ;
case CHAR_TEX_CONCRETE :
flPenetrationModifier = 0.4 ;
flDamageModifier = 0.25 ;
break ;
case CHAR_TEX_GRATE :
flPenetrationModifier = 1.0 ;
flDamageModifier = 0.99 ;
break ;
case CHAR_TEX_VENT :
flPenetrationModifier = 0.5 ;
flDamageModifier = 0.45 ;
break ;
case CHAR_TEX_TILE :
flPenetrationModifier = 0.65 ;
flDamageModifier = 0.3 ;
break ;
case CHAR_TEX_COMPUTER :
flPenetrationModifier = 0.4 ;
flDamageModifier = 0.45 ;
break ;
case CHAR_TEX_WOOD :
flPenetrationModifier = 1.0 ;
flDamageModifier = 0.6 ;
break ;
default :
flPenetrationModifier = 1.0 ;
flDamageModifier = 0.5 ;
break ;
}
Assert ( flPenetrationModifier > 0 ) ;
Assert ( flDamageModifier < 1.0f ) ; // Less than 1.0f for avoiding infinite loops
}
static bool TraceToExit ( Vector & start , Vector & dir , Vector & end , float flStepSize , float flMaxDistance )
{
float flDistance = 0 ;
Vector last = start ;
while ( flDistance < = flMaxDistance )
{
flDistance + = flStepSize ;
end = start + flDistance * dir ;
if ( ( UTIL_PointContents ( end ) & MASK_SOLID ) = = 0 )
{
// found first free point
return true ;
}
}
return false ;
}
inline void UTIL_TraceLineIgnoreTwoEntities ( const Vector & vecAbsStart , const Vector & vecAbsEnd , unsigned int mask ,
const IHandleEntity * ignore , const IHandleEntity * ignore2 , int collisionGroup , trace_t * ptr )
{
Ray_t ray ;
ray . Init ( vecAbsStart , vecAbsEnd ) ;
CTraceFilterSkipTwoEntities traceFilter ( ignore , ignore2 , collisionGroup ) ;
enginetrace - > TraceRay ( ray , mask , & traceFilter , ptr ) ;
if ( r_visualizetraces . GetBool ( ) )
{
DebugDrawLine ( ptr - > startpos , ptr - > endpos , 255 , 0 , 0 , true , - 1.0f ) ;
}
}
void CCSPlayer : : FireBullet (
Vector vecSrc , // shooting postion
const QAngle & shootAngles , //shooting angle
float flDistance , // max distance
int iPenetration , // how many obstacles can be penetrated
int iBulletType , // ammo type
int iDamage , // base damage
float flRangeModifier , // damage range modifier
CBaseEntity * pevAttacker , // shooter
bool bDoEffects ,
float xSpread , float ySpread
)
{
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 ) ;
// MIKETODO: put all the ammo parameters into a script file and allow for CS-specific params.
float flPenetrationPower = 0 ; // thickness of a wall that this bullet can penetrate
float flPenetrationDistance = 0 ; // distance at which the bullet is capable of penetrating a wall
float flDamageModifier = 0.5 ; // default modification of bullets power after they go through a wall.
float flPenetrationModifier = 1.f ;
GetBulletTypeParameters ( iBulletType , flPenetrationPower , flPenetrationDistance ) ;
if ( ! pevAttacker )
pevAttacker = this ; // the default attacker is ourselves
2024-01-23 14:32:02 +01:00
if ( weapon_accuracy_nospread . GetBool ( ) )
{
xSpread = 0.0f ;
ySpread = 0.0f ;
}
2020-04-22 18:56:21 +02:00
// add the spray
Vector vecDir = vecDirShooting + xSpread * vecRight + ySpread * vecUp ;
VectorNormalize ( vecDir ) ;
//Adrian: visualize server/client player positions
//This is used to show where the lag compesator thinks the player should be at.
#if 0
for ( int k = 1 ; k < = gpGlobals - > maxClients ; k + + )
{
CBasePlayer * clientClass = ( CBasePlayer * ) CBaseEntity : : Instance ( k ) ;
if ( clientClass = = NULL )
continue ;
if ( k = = entindex ( ) )
continue ;
# ifdef CLIENT_DLL
debugoverlay - > AddBoxOverlay ( clientClass - > GetAbsOrigin ( ) , clientClass - > WorldAlignMins ( ) , clientClass - > WorldAlignMaxs ( ) , QAngle ( 0 , 0 , 0 ) , 255 , 0 , 0 , 127 , 4 ) ;
# else
NDebugOverlay : : Box ( clientClass - > GetAbsOrigin ( ) , clientClass - > WorldAlignMins ( ) , clientClass - > WorldAlignMaxs ( ) , 0 , 0 , 255 , 127 , 4 ) ;
# endif
}
# endif
//=============================================================================
// HPE_BEGIN:
//=============================================================================
# ifndef CLIENT_DLL
// [pfreese] Track number player entities killed with this bullet
int iPenetrationKills = 0 ;
// [menglish] Increment the shots fired for this player
CCS_GameStats . Event_ShotFired ( this , GetActiveWeapon ( ) ) ;
# endif
//=============================================================================
// HPE_END
//=============================================================================
bool bFirstHit = true ;
CBasePlayer * lastPlayerHit = NULL ;
if ( sv_showplayerhitboxes . GetInt ( ) > 0 )
{
CBasePlayer * lagPlayer = UTIL_PlayerByIndex ( sv_showplayerhitboxes . GetInt ( ) ) ;
if ( lagPlayer )
{
# ifdef CLIENT_DLL
2024-04-03 05:14:06 +02:00
lagPlayer - > DrawClientHitboxes ( 60 , true ) ;
DevMsg ( " Client: %s => %f %f %f => %f (%f) \n " , lagPlayer - > GetPlayerName ( ) , lagPlayer - > GetAbsOrigin ( ) . x , lagPlayer - > GetAbsOrigin ( ) . y , lagPlayer - > GetAbsOrigin ( ) . z ,
lagPlayer - > m_flInterpolatedSimulationTime , fmod ( lagPlayer - > m_flInterpolatedSimulationTime , TICK_INTERVAL ) / TICK_INTERVAL ) ;
2020-04-22 18:56:21 +02:00
# else
2024-04-03 05:14:06 +02:00
lagPlayer - > DrawServerHitboxes ( 60 , true ) ;
2020-04-22 18:56:21 +02:00
# endif
}
}
MDLCACHE_CRITICAL_SECTION ( ) ;
while ( fCurrentDamage > 0 )
{
Vector vecEnd = vecSrc + vecDir * flDistance ;
trace_t tr ; // main enter bullet trace
UTIL_TraceLineIgnoreTwoEntities ( vecSrc , vecEnd , CS_MASK_SHOOT | CONTENTS_HITBOX , this , lastPlayerHit , COLLISION_GROUP_NONE , & tr ) ;
{
CTraceFilterSkipTwoEntities filter ( this , lastPlayerHit , COLLISION_GROUP_NONE ) ;
// Check for player hitboxes extending outside their collision bounds
const float rayExtension = 40.0f ;
UTIL_ClipTraceToPlayers ( vecSrc , vecEnd + vecDir * rayExtension , CS_MASK_SHOOT | CONTENTS_HITBOX , & filter , & tr ) ;
}
lastPlayerHit = ToBasePlayer ( tr . m_pEnt ) ;
if ( tr . fraction = = 1.0f )
break ; // we didn't hit anything, stop tracing shoot
# ifdef _DEBUG
if ( bFirstHit )
AddBulletStat ( gpGlobals - > realtime , VectorLength ( vecSrc - tr . endpos ) , tr . endpos ) ;
# endif
bFirstHit = false ;
# ifndef CLIENT_DLL
//
// Propogate a bullet impact event
// @todo Add this for shotgun pellets (which dont go thru here)
//
IGameEvent * event = gameeventmanager - > CreateEvent ( " bullet_impact " ) ;
if ( event )
{
event - > SetInt ( " userid " , GetUserID ( ) ) ;
event - > SetFloat ( " x " , tr . endpos . x ) ;
event - > SetFloat ( " y " , tr . endpos . y ) ;
event - > SetFloat ( " z " , tr . endpos . z ) ;
gameeventmanager - > FireEvent ( event ) ;
}
# endif
/************* MATERIAL DETECTION ***********/
surfacedata_t * pSurfaceData = physprops - > GetSurfaceData ( tr . surface . surfaceProps ) ;
int iEnterMaterial = pSurfaceData - > game . material ;
GetMaterialParameters ( iEnterMaterial , flPenetrationModifier , flDamageModifier ) ;
bool hitGrate = tr . contents & CONTENTS_GRATE ;
// since some railings in de_inferno are CONTENTS_GRATE but CHAR_TEX_CONCRETE, we'll trust the
// CONTENTS_GRATE and use a high damage modifier.
if ( hitGrate )
{
// If we're a concrete grate (TOOLS/TOOLSINVISIBLE texture) allow more penetrating power.
flPenetrationModifier = 1.0f ;
flDamageModifier = 0.99f ;
}
# ifdef CLIENT_DLL
if ( sv_showimpacts . GetInt ( ) = = 1 | | sv_showimpacts . GetInt ( ) = = 2 )
{
// 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
if ( sv_showimpacts . GetInt ( ) = = 1 | | sv_showimpacts . GetInt ( ) = = 3 )
{
// 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 * flDistance ;
fCurrentDamage * = pow ( flRangeModifier , ( flCurrentDistance / 500 ) ) ;
// check if we reach penetration distance, no more penetrations after that
if ( flCurrentDistance > flPenetrationDistance & & iPenetration > 0 )
iPenetration = 0 ;
# ifndef CLIENT_DLL
// This just keeps track of sounds for AIs (it doesn't play anything).
CSoundEnt : : InsertSound ( SOUND_BULLET_IMPACT , tr . endpos , 400 , 0.2f , this ) ;
# endif
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 - > GetTeamNumber ( ) = = GetTeamNumber ( ) ) )
{
UTIL_ImpactTrace ( & tr , iDamageType ) ;
}
}
}
} // bDoEffects
// add damage to entity that we hit
# ifndef CLIENT_DLL
ClearMultiDamage ( ) ;
//=============================================================================
// HPE_BEGIN:
// [pfreese] Check if enemy players were killed by this bullet, and if so,
// add them to the iPenetrationKills count
//=============================================================================
CBaseEntity * pEntity = tr . m_pEnt ;
CTakeDamageInfo info ( pevAttacker , pevAttacker , fCurrentDamage , iDamageType ) ;
CalculateBulletDamageForce ( & info , iBulletType , vecDir , tr . endpos ) ;
pEntity - > DispatchTraceAttack ( info , vecDir , & tr ) ;
bool bWasAlive = pEntity - > IsAlive ( ) ;
TraceAttackToTriggers ( info , tr . startpos , tr . endpos , vecDir ) ;
ApplyMultiDamage ( ) ;
if ( bWasAlive & & ! pEntity - > IsAlive ( ) & & pEntity - > IsPlayer ( ) & & pEntity - > GetTeamNumber ( ) ! = GetTeamNumber ( ) )
{
+ + iPenetrationKills ;
}
//=============================================================================
// HPE_END
//=============================================================================
# endif
// check if bullet can penetrate another entity
if ( iPenetration = = 0 & & ! hitGrate )
break ; // no, stop
// If we hit a grate with iPenetration == 0, stop on the next thing we hit
if ( iPenetration < 0 )
break ;
Vector penetrationEnd ;
// try to penetrate object, maximum penetration is 128 inch
if ( ! TraceToExit ( tr . endpos , vecDir , penetrationEnd , 24 , 128 ) )
break ;
// find exact penetration exit
trace_t exitTr ;
UTIL_TraceLine ( penetrationEnd , tr . endpos , CS_MASK_SHOOT | CONTENTS_HITBOX , NULL , & exitTr ) ;
if ( exitTr . m_pEnt ! = tr . m_pEnt & & exitTr . m_pEnt ! = NULL )
{
// something was blocking, trace again
UTIL_TraceLine ( penetrationEnd , tr . endpos , CS_MASK_SHOOT | CONTENTS_HITBOX , exitTr . m_pEnt , COLLISION_GROUP_NONE , & exitTr ) ;
}
// get material at exit point
pSurfaceData = physprops - > GetSurfaceData ( exitTr . surface . surfaceProps ) ;
int iExitMaterial = pSurfaceData - > game . material ;
hitGrate = hitGrate & & ( exitTr . contents & CONTENTS_GRATE ) ;
// if enter & exit point is wood or metal we assume this is
// a hollow crate or barrel and give a penetration bonus
if ( iEnterMaterial = = iExitMaterial )
{
if ( iExitMaterial = = CHAR_TEX_WOOD | |
iExitMaterial = = CHAR_TEX_METAL )
{
flPenetrationModifier * = 2 ;
}
}
float flTraceDistance = VectorLength ( exitTr . endpos - tr . endpos ) ;
// check if bullet has enough power to penetrate this distance for this material
if ( flTraceDistance > ( flPenetrationPower * flPenetrationModifier ) )
break ; // bullet hasn't enough power to penetrate this distance
// penetration was successful
// bullet did penetrate object, exit Decal
if ( bDoEffects )
{
UTIL_ImpactTrace ( & exitTr , iDamageType ) ;
}
//setup new start end parameters for successive trace
flPenetrationPower - = flTraceDistance / flPenetrationModifier ;
flCurrentDistance + = flTraceDistance ;
// NDebugOverlay::Box( exitTr.endpos, Vector(-2,-2,-2), Vector(2,2,2), 0,255,0,127, 8 );
vecSrc = exitTr . endpos ;
flDistance = ( flDistance - flCurrentDistance ) * 0.5 ;
// reduce damage power each time we hit something other than a grate
fCurrentDamage * = flDamageModifier ;
// reduce penetration counter
iPenetration - - ;
}
# ifndef CLIENT_DLL
//=============================================================================
// HPE_BEGIN:
// [pfreese] If we killed at least two enemies with a single bullet, award the
// TWO_WITH_ONE_SHOT achievement
//=============================================================================
if ( iPenetrationKills > = 2 )
{
AwardAchievement ( CSKillTwoWithOneShot ) ;
}
//=============================================================================
// HPE_END
//=============================================================================
# endif
}
void CCSPlayer : : UpdateStepSound ( surfacedata_t * psurface , const Vector & vecOrigin , const Vector & vecVelocity )
{
float speedSqr = vecVelocity . AsVector2D ( ) . LengthSqr ( ) ;
// the fastest walk is 135 ( scout ), see CCSGameMovement::CheckParameters()
if ( speedSqr < 150.0 * 150.0 )
return ; // player is not running, no footsteps
BaseClass : : UpdateStepSound ( psurface , vecOrigin , vecVelocity ) ;
}
// GOOSEMAN : Kick the view..
void CCSPlayer : : KickBack ( float up_base , float lateral_base , float up_modifier , float lateral_modifier , float up_max , float lateral_max , int direction_change )
{
float flKickUp ;
float flKickLateral ;
if ( m_iShotsFired = = 1 ) // This is the first round fired
{
flKickUp = up_base ;
flKickLateral = lateral_base ;
}
else
{
flKickUp = up_base + m_iShotsFired * up_modifier ;
flKickLateral = lateral_base + m_iShotsFired * lateral_modifier ;
}
QAngle angle = GetPunchAngle ( ) ;
angle . x - = flKickUp ;
if ( angle . x < - 1 * up_max )
angle . x = - 1 * up_max ;
if ( m_iDirection = = 1 )
{
angle . y + = flKickLateral ;
if ( angle . y > lateral_max )
angle . y = lateral_max ;
}
else
{
angle . y - = flKickLateral ;
if ( angle . y < - 1 * lateral_max )
angle . y = - 1 * lateral_max ;
}
if ( ! SharedRandomInt ( " KickBack " , 0 , direction_change ) )
m_iDirection = 1 - m_iDirection ;
SetPunchAngle ( angle ) ;
}
bool CCSPlayer : : CanMove ( ) const
{
// When we're in intro camera mode, it's important to return false here
// so our physics object doesn't fall out of the world.
if ( GetMoveType ( ) = = MOVETYPE_NONE )
return false ;
if ( IsObserver ( ) )
return true ; // observers can move all the time
bool bValidMoveState = ( State_Get ( ) = = STATE_ACTIVE | | State_Get ( ) = = STATE_OBSERVER_MODE ) ;
if ( m_bIsDefusing | | ! bValidMoveState | | CSGameRules ( ) - > IsFreezePeriod ( ) )
{
return false ;
}
else
{
// Can't move while planting C4.
CC4 * pC4 = dynamic_cast < CC4 * > ( GetActiveWeapon ( ) ) ;
if ( pC4 & & pC4 - > m_bStartedArming )
return false ;
return true ;
}
}
void CCSPlayer : : OnJump ( float fImpulse )
{
CWeaponCSBase * pActiveWeapon = GetActiveCSWeapon ( ) ;
if ( pActiveWeapon ! = NULL )
pActiveWeapon - > OnJump ( fImpulse ) ;
}
void CCSPlayer : : OnLand ( float fVelocity )
{
CWeaponCSBase * pActiveWeapon = GetActiveCSWeapon ( ) ;
if ( pActiveWeapon ! = NULL )
pActiveWeapon - > OnLand ( fVelocity ) ;
}
//-------------------------------------------------------------------------------------------------------------------------------
/**
* Track the last time we were on a ladder , along with the ladder ' s normal and where we
* were grabbing it , so we don ' t reach behind us and grab it again as we are trying to
* dismount .
*/
void CCSPlayer : : SurpressLadderChecks ( const Vector & pos , const Vector & normal )
{
m_ladderSurpressionTimer . Start ( 1.0f ) ;
m_lastLadderPos = pos ;
m_lastLadderNormal = normal ;
}
//-------------------------------------------------------------------------------------------------------------------------------
/**
* Prevent us from re - grabbing the same ladder we were just on :
* - if the timer is elapsed , let us grab again
* - if the normal is different , let us grab
* - if the 2 D pos is very different , let us grab , since it ' s probably a different ladder
*/
bool CCSPlayer : : CanGrabLadder ( const Vector & pos , const Vector & normal )
{
if ( m_ladderSurpressionTimer . GetRemainingTime ( ) < = 0.0f )
{
return true ;
}
const float MaxDist = 64.0f ;
if ( pos . AsVector2D ( ) . DistToSqr ( m_lastLadderPos . AsVector2D ( ) ) < MaxDist * MaxDist )
{
return false ;
}
if ( normal ! = m_lastLadderNormal )
{
return true ;
}
return false ;
}
void CCSPlayer : : SetAnimation ( PLAYER_ANIM playerAnim )
{
// In CS, its CPlayerAnimState object manages ALL the animation state.
return ;
}
CWeaponCSBase * CCSPlayer : : CSAnim_GetActiveWeapon ( )
{
return GetActiveCSWeapon ( ) ;
}
bool CCSPlayer : : CSAnim_CanMove ( )
{
return CanMove ( ) ;
}
//--------------------------------------------------------------------------------------------------------------
# define MATERIAL_NAME_LENGTH 16
# ifdef GAME_DLL
class CFootstepControl : public CBaseTrigger
{
public :
DECLARE_CLASS ( CFootstepControl , CBaseTrigger ) ;
DECLARE_DATADESC ( ) ;
DECLARE_SERVERCLASS ( ) ;
virtual int UpdateTransmitState ( void ) ;
virtual void Spawn ( void ) ;
CNetworkVar ( string_t , m_source ) ;
CNetworkVar ( string_t , m_destination ) ;
} ;
LINK_ENTITY_TO_CLASS ( func_footstep_control , CFootstepControl ) ;
BEGIN_DATADESC ( CFootstepControl )
DEFINE_KEYFIELD ( m_source , FIELD_STRING , " Source " ) ,
DEFINE_KEYFIELD ( m_destination , FIELD_STRING , " Destination " ) ,
END_DATADESC ( )
IMPLEMENT_SERVERCLASS_ST ( CFootstepControl , DT_FootstepControl )
SendPropStringT ( SENDINFO ( m_source ) ) ,
SendPropStringT ( SENDINFO ( m_destination ) ) ,
END_SEND_TABLE ( )
int CFootstepControl : : UpdateTransmitState ( void )
{
return SetTransmitState ( FL_EDICT_ALWAYS ) ;
}
void CFootstepControl : : Spawn ( void )
{
InitTrigger ( ) ;
}
# else
//--------------------------------------------------------------------------------------------------------------
class C_FootstepControl : public C_BaseEntity
{
public :
DECLARE_CLASS ( C_FootstepControl , C_BaseEntity ) ;
DECLARE_CLIENTCLASS ( ) ;
C_FootstepControl ( void ) ;
~ C_FootstepControl ( ) ;
char m_source [ MATERIAL_NAME_LENGTH ] ;
char m_destination [ MATERIAL_NAME_LENGTH ] ;
} ;
IMPLEMENT_CLIENTCLASS_DT ( C_FootstepControl , DT_FootstepControl , CFootstepControl )
RecvPropString ( RECVINFO ( m_source ) ) ,
RecvPropString ( RECVINFO ( m_destination ) ) ,
END_RECV_TABLE ( )
CUtlVector < C_FootstepControl * > s_footstepControllers ;
C_FootstepControl : : C_FootstepControl ( void )
{
s_footstepControllers . AddToTail ( this ) ;
}
C_FootstepControl : : ~ C_FootstepControl ( )
{
s_footstepControllers . FindAndRemove ( this ) ;
}
surfacedata_t * CCSPlayer : : GetFootstepSurface ( const Vector & origin , const char * surfaceName )
{
for ( int i = 0 ; i < s_footstepControllers . Count ( ) ; + + i )
{
C_FootstepControl * control = s_footstepControllers [ i ] ;
if ( FStrEq ( control - > m_source , surfaceName ) )
{
if ( control - > CollisionProp ( ) - > IsPointInBounds ( origin ) )
{
return physprops - > GetSurfaceData ( physprops - > GetSurfaceIndex ( control - > m_destination ) ) ;
}
}
}
return physprops - > GetSurfaceData ( physprops - > GetSurfaceIndex ( surfaceName ) ) ;
}
# endif