diff options
Diffstat (limited to 'game/shared/cstrike/cs_player_shared.cpp')
| -rw-r--r-- | game/shared/cstrike/cs_player_shared.cpp | 943 |
1 files changed, 943 insertions, 0 deletions
diff --git a/game/shared/cstrike/cs_player_shared.cpp b/game/shared/cstrike/cs_player_shared.cpp new file mode 100644 index 0000000..e0f3d91 --- /dev/null +++ b/game/shared/cstrike/cs_player_shared.cpp @@ -0,0 +1,943 @@ +//========= 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." ); + +#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 + + // 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 + lagPlayer->DrawClientHitboxes(4, true); +#else + lagPlayer->DrawServerHitboxes(4, true); +#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 2D 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 + + |