source-engine/game/shared/dod/dod_player_shared.cpp
2022-04-16 12:05:19 +03:00

1397 lines
32 KiB
C++

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#include "cbase.h"
#include "dod_gamerules.h"
#include "takedamageinfo.h"
#include "dod_shareddefs.h"
#include "effect_dispatch_data.h"
#include "weapon_dodbase.h"
#include "weapon_dodbipodgun.h"
#include "weapon_dodbaserpg.h"
#include "weapon_dodsniper.h"
#include "movevars_shared.h"
#include "engine/IEngineSound.h"
#include "SoundEmitterSystem/isoundemittersystembase.h"
#include "engine/ivdebugoverlay.h"
#include "obstacle_pushaway.h"
#include "props_shared.h"
#include "decals.h"
#include "util_shared.h"
#ifdef CLIENT_DLL
#include "c_dod_player.h"
#include "prediction.h"
#include "clientmode_dod.h"
#include "vgui_controls/AnimationController.h"
#define CRecipientFilter C_RecipientFilter
#else
#include "dod_player.h"
#endif
ConVar dod_bonusround( "dod_bonusround", "1", FCVAR_REPLICATED, "If true, the winners of the round can attack in the intermission." );
ConVar sv_showimpacts("sv_showimpacts", "0", FCVAR_REPLICATED | FCVAR_CHEAT, "Shows client (red) and server (blue) bullet impact point" );
void DispatchEffect( const char *pName, const CEffectData &data );
bool CDODPlayer::CanMove( void ) const
{
bool bValidMoveState = (State_Get() == STATE_ACTIVE || State_Get() == STATE_OBSERVER_MODE);
if ( !bValidMoveState )
{
return false;
}
return true;
}
// BUG! This is not called on the client at respawn, only first spawn!
void CDODPlayer::SharedSpawn()
{
BaseClass::SharedSpawn();
// Reset the animation state or we will animate to standing
// when we spawn
m_Shared.SetJumping( false );
m_flMinNextStepSoundTime = gpGlobals->curtime;
m_bPlayingProneMoveSound = false;
}
float GetDensityFromMaterial( surfacedata_t *pSurfaceData )
{
float flMaterialMod = 1.0f;
Assert( pSurfaceData );
// material mod is how many points of damage it costs to go through
// 1 unit of the material
switch( pSurfaceData->game.material )
{
//super soft
// case CHAR_TEX_LEAVES:
// flMaterialMod = 1.2f;
// break;
case CHAR_TEX_FLESH:
flMaterialMod = 1.35f;
break;
//soft
// case CHAR_TEX_STUCCO:
// case CHAR_TEX_SNOW:
case CHAR_TEX_GLASS:
case CHAR_TEX_WOOD:
case CHAR_TEX_TILE:
flMaterialMod = 1.8f;
break;
//hard
// case CHAR_TEX_SKY:
// case CHAR_TEX_ROCK:
// case CHAR_TEX_SAND:
case CHAR_TEX_CONCRETE:
case CHAR_TEX_DIRT: // "sand"
flMaterialMod = 6.6f;
break;
//really hard
// case CHAR_TEX_HEAVYMETAL:
case CHAR_TEX_GRATE:
case CHAR_TEX_METAL:
flMaterialMod = 13.5f;
break;
case 'X': // invisible collision material
flMaterialMod = 0.1f;
break;
//medium
// case CHAR_TEX_BRICK:
// case CHAR_TEX_GRAVEL:
// case CHAR_TEX_GRASS:
default:
#ifndef CLIENT_DLL
AssertMsg( 0, UTIL_VarArgs( "Material has unknown materialmod - '%c' \n", pSurfaceData->game.material ) );
#endif
flMaterialMod = 5.0f;
break;
}
Assert( flMaterialMod > 0 );
return flMaterialMod;
}
static bool TraceToExit( const Vector &start,
const Vector &dir,
Vector &end,
const float flStepSize,
const float flMaxDistance )
{
float flDistance = 0;
Vector last = start;
while ( flDistance < flMaxDistance )
{
flDistance += flStepSize;
// no point in tracing past the max distance.
// if this check fails, we save ourselves a traceline later
if ( flDistance > flMaxDistance )
{
flDistance = flMaxDistance;
}
end = start + flDistance * dir;
// point contents fails to return proper contents inside a func_detail brush, eg the dod_flash
// stairs
//int contents = UTIL_PointContents( end );
trace_t tr;
UTIL_TraceLine( end, end, MASK_SOLID | CONTENTS_HITBOX, NULL, &tr );
//if ( (UTIL_PointContents ( end ) & MASK_SOLID) == 0 )
if ( !tr.startsolid )
{
// found first free point
return true;
}
}
return false;
}
#include "ammodef.h"
#define NEW_HITBOX_GROUP_CODE 1
#undef ARM_PENETRATION
#ifndef CLIENT_DLL
#include "ndebugoverlay.h"
#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_flDamage; //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
int 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 CDODPlayer::SetSprinting( bool bIsSprinting )
{
m_Shared.SetSprinting( bIsSprinting );
}
bool CDODPlayer::IsSprinting( void )
{
float flVelSqr = GetAbsVelocity().LengthSqr();
return m_Shared.m_bIsSprinting && ( flVelSqr > 0.5f );
}
bool CDODPlayer::CanAttack( void )
{
if ( IsSprinting() )
return false;
if ( GetMoveType() == MOVETYPE_LADDER )
return false;
if ( m_Shared.IsJumping() )
return false;
if ( m_Shared.IsDefusing() )
return false;
// cannot attack while prone moving. except if you have a bazooka
if ( m_Shared.IsProne() && GetAbsVelocity().LengthSqr() > 1 )
{
return false;
}
if( m_Shared.IsGoingProne() || m_Shared.IsGettingUpFromProne() )
{
return false;
}
CDODGameRules *rules = DODGameRules();
Assert( rules );
DODRoundState state = rules->State_Get();
if ( dod_bonusround.GetBool() )
{
if ( GetTeamNumber() == TEAM_ALLIES )
{
return ( state == STATE_RND_RUNNING || state == STATE_ALLIES_WIN );
}
else
{
return ( state == STATE_RND_RUNNING || state == STATE_AXIS_WIN );
}
}
else
return ( state == STATE_RND_RUNNING );
}
void CDODPlayer::SetAnimation( PLAYER_ANIM playerAnim )
{
// In DoD, its CPlayerAnimState object manages ALL the animation state.
return;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input :
// Output : const Vector
//-----------------------------------------------------------------------------
const Vector CDODPlayer::GetPlayerMins( void ) const
{
if ( IsObserver() )
{
return VEC_OBS_HULL_MIN;
}
else
{
if ( GetFlags() & FL_DUCKING )
{
return VEC_DUCK_HULL_MIN;
}
else if ( m_Shared.IsProne() )
{
return VEC_PRONE_HULL_MIN;
}
else
{
return VEC_HULL_MIN;
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input :
// Output : const Vector
//-----------------------------------------------------------------------------
const Vector CDODPlayer::GetPlayerMaxs( void ) const
{
if ( IsObserver() )
{
return VEC_OBS_HULL_MAX_SCALED( this );
}
else
{
if ( GetFlags() & FL_DUCKING )
{
return VEC_DUCK_HULL_MAX_SCALED( this );
}
else if ( m_Shared.IsProne() )
{
return VEC_PRONE_HULL_MAX_SCALED( this );
}
else
{
return VEC_HULL_MAX_SCALED( this );
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : collisionGroup -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CDODPlayer::ShouldCollide( int collisionGroup, int contentsMask ) const
{
if ( collisionGroup == COLLISION_GROUP_PLAYER_MOVEMENT || collisionGroup == COLLISION_GROUP_PROJECTILE )
{
switch( GetTeamNumber() )
{
case TEAM_ALLIES:
if ( !( contentsMask & CONTENTS_TEAM2 ) )
return false;
break;
case TEAM_AXIS:
if ( !( contentsMask & CONTENTS_TEAM1 ) )
return false;
break;
}
}
return BaseClass::ShouldCollide( collisionGroup, contentsMask );
}
// --------------------------------------------------------------------------------------------------- //
// CDODPlayerShared implementation.
// --------------------------------------------------------------------------------------------------- //
CDODPlayerShared::CDODPlayerShared()
{
m_bProne = false;
m_bForceProneChange = false;
m_flNextProneCheck = 0;
m_flSlowedUntilTime = 0;
m_flUnProneTime = 0;
m_flGoProneTime = 0;
m_flDeployedHeight = STANDING_DEPLOY_HEIGHT;
m_flDeployChangeTime = gpGlobals->curtime;
SetDesiredPlayerClass( PLAYERCLASS_UNDEFINED );
m_flLastViewAnimationTime = gpGlobals->curtime;
m_pViewOffsetAnim = NULL;
}
CDODPlayerShared::~CDODPlayerShared()
{
if ( m_pViewOffsetAnim )
{
delete m_pViewOffsetAnim;
m_pViewOffsetAnim = NULL;
}
}
void CDODPlayerShared::Init( CDODPlayer *pPlayer )
{
m_pOuter = pPlayer;
}
bool CDODPlayerShared::IsDucking( void ) const
{
return !!( m_pOuter->GetFlags() & FL_DUCKING );
}
bool CDODPlayerShared::IsProne() const
{
return m_bProne;
}
bool CDODPlayerShared::IsGettingUpFromProne() const
{
return ( m_flUnProneTime > 0 );
}
bool CDODPlayerShared::IsGoingProne() const
{
return ( m_flGoProneTime > 0 );
}
void CDODPlayerShared::SetSprinting( bool bSprinting )
{
if ( bSprinting && !m_bIsSprinting )
{
StartSprinting();
// only one penalty per key press
if ( m_bGaveSprintPenalty == false )
{
m_flStamina -= INITIAL_SPRINT_STAMINA_PENALTY;
m_bGaveSprintPenalty = true;
}
}
else if ( !bSprinting && m_bIsSprinting )
{
StopSprinting();
}
}
// this is reset when we let go of the sprint key
void CDODPlayerShared::ResetSprintPenalty( void )
{
m_bGaveSprintPenalty = false;
}
void CDODPlayerShared::StartSprinting( void )
{
m_bIsSprinting = true;
#ifndef CLIENT_DLL
m_pOuter->RemoveHintTimer( HINT_USE_SPRINT );
#endif
}
void CDODPlayerShared::StopSprinting( void )
{
m_bIsSprinting = false;
}
void CDODPlayerShared::SetProne( bool bProne, bool bNoAnimation /* = false */ )
{
m_bProne = bProne;
if ( bNoAnimation )
{
m_flGoProneTime = 0;
m_flUnProneTime = 0;
// cancel the view animation!
m_bForceProneChange = true;
}
if ( !bProne /*&& IsSniperZoomed()*/ ) // forceunzoom for going prone is in StartGoingProne
{
ForceUnzoom();
}
}
void CDODPlayerShared::SetJumping( bool bJumping )
{
m_bJumping = bJumping;
if ( IsSniperZoomed() )
{
ForceUnzoom();
}
}
void CDODPlayerShared::ForceUnzoom( void )
{
CWeaponDODBase *pWeapon = GetActiveDODWeapon();
if( pWeapon && ( pWeapon->GetDODWpnData().m_WeaponType & WPN_MASK_GUN ) )
{
CDODSniperWeapon *pSniper = dynamic_cast<CDODSniperWeapon *>(pWeapon);
if ( pSniper )
{
pSniper->ZoomOut();
}
}
}
bool CDODPlayerShared::IsBazookaDeployed( void ) const
{
CWeaponDODBase *pWeapon = GetActiveDODWeapon();
if( pWeapon && pWeapon->GetDODWpnData().m_WeaponType == WPN_TYPE_BAZOOKA )
{
CDODBaseRocketWeapon *pBazooka = (CDODBaseRocketWeapon *)pWeapon;
return pBazooka->IsDeployed() && !pBazooka->m_bInReload;
}
return false;
}
bool CDODPlayerShared::IsBazookaOnlyDeployed( void ) const
{
CWeaponDODBase *pWeapon = GetActiveDODWeapon();
if( pWeapon && pWeapon->GetDODWpnData().m_WeaponType == WPN_TYPE_BAZOOKA )
{
CDODBaseRocketWeapon *pBazooka = (CDODBaseRocketWeapon *)pWeapon;
return pBazooka->IsDeployed();
}
return false;
}
bool CDODPlayerShared::IsSniperZoomed( void ) const
{
CWeaponDODBase *pWeapon = GetActiveDODWeapon();
if( pWeapon && ( pWeapon->GetDODWpnData().m_WeaponType & WPN_MASK_GUN ) )
{
CWeaponDODBaseGun *pGun = (CWeaponDODBaseGun *)pWeapon;
Assert( pGun );
return pGun->IsSniperZoomed();
}
return false;
}
bool CDODPlayerShared::IsInMGDeploy( void ) const
{
CWeaponDODBase *pWeapon = GetActiveDODWeapon();
if( pWeapon && pWeapon->GetDODWpnData().m_WeaponType == WPN_TYPE_MG )
{
CDODBipodWeapon *pMG = dynamic_cast<CDODBipodWeapon *>( pWeapon );
Assert( pMG );
return pMG->IsDeployed();
}
return false;
}
bool CDODPlayerShared::IsProneDeployed( void ) const
{
return ( IsProne() && IsInMGDeploy() );
}
bool CDODPlayerShared::IsSandbagDeployed( void ) const
{
return ( !IsProne() && IsInMGDeploy() );
}
void CDODPlayerShared::SetDesiredPlayerClass( int playerclass )
{
m_iDesiredPlayerClass = playerclass;
}
int CDODPlayerShared::DesiredPlayerClass( void )
{
return m_iDesiredPlayerClass;
}
void CDODPlayerShared::SetPlayerClass( int playerclass )
{
m_iPlayerClass = playerclass;
}
int CDODPlayerShared::PlayerClass( void )
{
return m_iPlayerClass;
}
void CDODPlayerShared::SetStamina( float flStamina )
{
m_flStamina = clamp( flStamina, 0, 100 );
}
CWeaponDODBase* CDODPlayerShared::GetActiveDODWeapon() const
{
CBaseCombatWeapon *pWeapon = m_pOuter->GetActiveWeapon();
if ( pWeapon )
{
Assert( dynamic_cast< CWeaponDODBase* >( pWeapon ) == static_cast< CWeaponDODBase* >( pWeapon ) );
return static_cast< CWeaponDODBase* >( pWeapon );
}
else
{
return NULL;
}
}
void CDODPlayerShared::SetDeployed( bool bDeployed, float flHeight /* = -1 */ )
{
if( gpGlobals->curtime - m_flDeployChangeTime < 0.2 )
{
Assert(0);
}
m_flDeployChangeTime = gpGlobals->curtime;
m_vecDeployedAngles = m_pOuter->EyeAngles();
if( flHeight > 0 )
{
m_flDeployedHeight = flHeight;
}
else
{
m_flDeployedHeight = m_pOuter->GetViewOffset()[2];
}
}
QAngle CDODPlayerShared::GetDeployedAngles( void ) const
{
return m_vecDeployedAngles;
}
void CDODPlayerShared::SetDeployedYawLimits( float flLeftYaw, float flRightYaw )
{
m_flDeployedYawLimitLeft = flLeftYaw;
m_flDeployedYawLimitRight = -flRightYaw;
m_vecDeployedAngles = m_pOuter->EyeAngles();
}
void CDODPlayerShared::ClampDeployedAngles( QAngle *vecTestAngles )
{
Assert( vecTestAngles );
// Clamp Pitch
vecTestAngles->x = clamp( vecTestAngles->x, MAX_DEPLOY_PITCH, MIN_DEPLOY_PITCH );
// Clamp Yaw - do a bit more work as yaw will wrap around and cause problems
float flDeployedYawCenter = GetDeployedAngles().y;
float flDelta = AngleNormalize( vecTestAngles->y - flDeployedYawCenter );
if( flDelta < m_flDeployedYawLimitRight )
{
vecTestAngles->y = flDeployedYawCenter + m_flDeployedYawLimitRight;
}
else if( flDelta > m_flDeployedYawLimitLeft )
{
vecTestAngles->y = flDeployedYawCenter + m_flDeployedYawLimitLeft;
}
/*
Msg( "delta %.1f ( left %.1f, right %.1f ) ( %.1f -> %.1f )\n",
flDelta,
flDeployedYawCenter + m_flDeployedYawLimitLeft,
flDeployedYawCenter + m_flDeployedYawLimitRight,
before,
vecTestAngles->y );
*/
}
float CDODPlayerShared::GetDeployedHeight( void ) const
{
return m_flDeployedHeight;
}
float CDODPlayerShared::GetSlowedTime( void ) const
{
return m_flSlowedUntilTime;
}
void CDODPlayerShared::SetSlowedTime( float t )
{
m_flSlowedUntilTime = gpGlobals->curtime + t;
}
void CDODPlayerShared::StartGoingProne( void )
{
// make the prone sound
CPASFilter filter( m_pOuter->GetAbsOrigin() );
filter.UsePredictionRules();
m_pOuter->EmitSound( filter, m_pOuter->entindex(), "Player.GoProne" );
// slow to prone speed
m_flGoProneTime = gpGlobals->curtime + TIME_TO_PRONE;
m_flUnProneTime = 0.0f; //reset
if ( IsSniperZoomed() )
ForceUnzoom();
}
void CDODPlayerShared::StandUpFromProne( void )
{
// make the prone sound
CPASFilter filter( m_pOuter->GetAbsOrigin() );
filter.UsePredictionRules();
m_pOuter->EmitSound( filter, m_pOuter->entindex(), "Player.UnProne" );
// speed up to target speed
m_flUnProneTime = gpGlobals->curtime + TIME_TO_PRONE;
m_flGoProneTime = 0.0f; //reset
}
bool CDODPlayerShared::CanChangePosition( void )
{
if ( IsInMGDeploy() )
return false;
if ( IsGettingUpFromProne() )
return false;
if ( IsGoingProne() )
return false;
return true;
}
void CDODPlayer::UpdateStepSound( surfacedata_t *psurface, const Vector &vecOrigin, const Vector &vecVelocity )
{
Vector knee;
Vector feet;
float height;
int fLadder;
if ( m_flStepSoundTime > 0 )
{
m_flStepSoundTime -= 1000.0f * gpGlobals->frametime;
if ( m_flStepSoundTime < 0 )
{
m_flStepSoundTime = 0;
}
}
if ( m_flStepSoundTime > 0 )
return;
if ( GetFlags() & (FL_FROZEN|FL_ATCONTROLS))
return;
if ( GetMoveType() == MOVETYPE_NOCLIP || GetMoveType() == MOVETYPE_OBSERVER )
return;
if ( !sv_footsteps.GetFloat() )
return;
float speed = VectorLength( vecVelocity );
float groundspeed = Vector2DLength( vecVelocity.AsVector2D() );
// determine if we are on a ladder
fLadder = ( GetMoveType() == MOVETYPE_LADDER );
float flDuck;
if ( ( GetFlags() & FL_DUCKING) || fLadder )
{
flDuck = 100;
}
else
{
flDuck = 0;
}
static float flMinProneSpeed = 10.0f;
static float flMinSpeed = 70.0f;
static float flRunSpeed = 110.0f;
bool onground = ( GetFlags() & FL_ONGROUND );
bool movingalongground = ( groundspeed > 0.0f );
bool moving_fast_enough = ( speed >= flMinSpeed );
// always play a step sound if we are moving faster than
// To hear step sounds you must be either on a ladder or moving along the ground AND
// You must be moving fast enough
CheckProneMoveSound( groundspeed, onground );
if ( !moving_fast_enough || !(fLadder || ( onground && movingalongground )) )
{
return;
}
bool bWalking = ( speed < flRunSpeed ); // or ducking!
VectorCopy( vecOrigin, knee );
VectorCopy( vecOrigin, feet );
height = GetPlayerMaxs()[ 2 ] - GetPlayerMins()[ 2 ];
knee[2] = vecOrigin[2] + 0.2 * height;
float flVol;
// find out what we're stepping in or on...
if ( fLadder )
{
psurface = physprops->GetSurfaceData( physprops->GetSurfaceIndex( "ladder" ) );
flVol = 1.0;
m_flStepSoundTime = 350;
}
else if ( enginetrace->GetPointContents( knee ) & MASK_WATER )
{
static int iSkipStep = 0;
if ( iSkipStep == 0 )
{
iSkipStep++;
return;
}
if ( iSkipStep++ == 3 )
{
iSkipStep = 0;
}
psurface = physprops->GetSurfaceData( physprops->GetSurfaceIndex( "wade" ) );
flVol = 0.65;
m_flStepSoundTime = 600;
}
else if ( enginetrace->GetPointContents( feet ) & MASK_WATER )
{
psurface = physprops->GetSurfaceData( physprops->GetSurfaceIndex( "water" ) );
flVol = bWalking ? 0.2 : 0.5;
m_flStepSoundTime = bWalking ? 400 : 300;
}
else
{
if ( !psurface )
return;
if ( bWalking )
{
m_flStepSoundTime = 400;
}
else
{
if ( speed > 200 )
{
int speeddiff = PLAYER_SPEED_SPRINT - PLAYER_SPEED_RUN;
int diff = speed - PLAYER_SPEED_RUN;
float percent = (float)diff / (float)speeddiff;
m_flStepSoundTime = 300.0f - 30.0f * percent;
}
else
{
m_flStepSoundTime = 400;
}
}
switch ( psurface->game.material )
{
default:
case CHAR_TEX_CONCRETE:
flVol = bWalking ? 0.2 : 0.5;
break;
case CHAR_TEX_METAL:
flVol = bWalking ? 0.2 : 0.5;
break;
case CHAR_TEX_DIRT:
flVol = bWalking ? 0.25 : 0.55;
break;
case CHAR_TEX_VENT:
flVol = bWalking ? 0.4 : 0.7;
break;
case CHAR_TEX_GRATE:
flVol = bWalking ? 0.2 : 0.5;
break;
case CHAR_TEX_TILE:
flVol = bWalking ? 0.2 : 0.5;
break;
case CHAR_TEX_SLOSH:
flVol = bWalking ? 0.2 : 0.5;
break;
}
}
m_flStepSoundTime += flDuck; // slower step time if ducking
if ( GetFlags() & FL_DUCKING )
{
flVol *= 0.65;
}
// protect us from prediction errors a little bit
if ( m_flMinNextStepSoundTime > gpGlobals->curtime )
{
return;
}
m_flMinNextStepSoundTime = gpGlobals->curtime + 0.1f;
PlayStepSound( feet, psurface, flVol, false );
}
void CDODPlayer::CheckProneMoveSound( int groundspeed, bool onground )
{
#ifdef CLIENT_DLL
bool bShouldPlay = (groundspeed > 10) && (onground == true) && m_Shared.IsProne() && IsAlive();
if ( m_bPlayingProneMoveSound && !bShouldPlay )
{
StopSound( "Player.MoveProne" );
m_bPlayingProneMoveSound= false;
}
else if ( !m_bPlayingProneMoveSound && bShouldPlay )
{
CRecipientFilter filter;
filter.AddRecipientsByPAS( WorldSpaceCenter() );
EmitSound( filter, entindex(), "Player.MoveProne" );
m_bPlayingProneMoveSound = true;
}
#endif
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : step -
// fvol -
// force - force sound to play
//-----------------------------------------------------------------------------
void CDODPlayer::PlayStepSound( Vector &vecOrigin, surfacedata_t *psurface, float fvol, bool force )
{
if ( gpGlobals->maxClients > 1 && !sv_footsteps.GetFloat() )
return;
#if defined( CLIENT_DLL )
// during prediction play footstep sounds only once
if ( prediction->InPrediction() && !prediction->IsFirstTimePredicted() )
return;
#endif
if ( !psurface )
return;
unsigned short stepSoundName = m_Local.m_nStepside ? psurface->sounds.stepleft : psurface->sounds.stepright;
m_Local.m_nStepside = !m_Local.m_nStepside;
if ( !stepSoundName )
return;
IPhysicsSurfaceProps *physprops = MoveHelper( )->GetSurfaceProps();
const char *pSoundName = physprops->GetString( stepSoundName );
CSoundParameters params;
// we don't always know the model, so go by team
char *pModelNameForGender = DOD_PLAYERMODEL_AXIS_RIFLEMAN;
if( GetTeamNumber() == TEAM_ALLIES )
pModelNameForGender = DOD_PLAYERMODEL_US_RIFLEMAN;
if ( !CBaseEntity::GetParametersForSound( pSoundName, params, pModelNameForGender ) )
return;
CRecipientFilter filter;
filter.AddRecipientsByPAS( vecOrigin );
#ifndef CLIENT_DLL
// im MP, server removed all players in origins PVS, these players
// generate the footsteps clientside
if ( gpGlobals->maxClients > 1 )
filter.RemoveRecipientsByPVS( vecOrigin );
#endif
EmitSound_t ep;
ep.m_nChannel = params.channel;
ep.m_pSoundName = params.soundname;
ep.m_flVolume = fvol;
ep.m_SoundLevel = params.soundlevel;
ep.m_nFlags = 0;
ep.m_nPitch = params.pitch;
ep.m_pOrigin = &vecOrigin;
EmitSound( filter, entindex(), ep );
}
Activity CDODPlayer::TranslateActivity( Activity baseAct, bool *pRequired /* = NULL */ )
{
Activity translated = baseAct;
if ( GetActiveWeapon() )
{
translated = GetActiveWeapon()->ActivityOverride( baseAct, pRequired );
}
else if (pRequired)
{
*pRequired = false;
}
return translated;
}
void CDODPlayerShared::SetCPIndex( int index )
{
#ifdef CLIENT_DLL
if ( m_pOuter->IsLocalPlayer() )
{
if ( index == -1 )
{
// just left an area
g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "ObjectiveIconShrink" );
}
else
{
g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "ObjectiveIconGrow" );
}
}
#endif
m_iCPIndex = index;
}
void CDODPlayerShared::SetLastViewAnimTime( float flTime )
{
m_flLastViewAnimationTime = flTime;
}
float CDODPlayerShared::GetLastViewAnimTime( void )
{
return m_flLastViewAnimationTime;
}
void CDODPlayerShared::ResetViewOffsetAnimation( void )
{
if ( m_pViewOffsetAnim )
{
//cancel it!
m_pViewOffsetAnim->Reset();
}
}
void CDODPlayerShared::ViewOffsetAnimation( Vector vecDest, float flTime, ViewAnimationType type )
{
if ( !m_pViewOffsetAnim )
{
m_pViewOffsetAnim = CViewOffsetAnimation::CreateViewOffsetAnim( m_pOuter );
}
Assert( m_pViewOffsetAnim );
if ( m_pViewOffsetAnim )
{
m_pViewOffsetAnim->StartAnimation( m_pOuter->GetViewOffset(), vecDest, flTime, type );
}
}
void CDODPlayerShared::ViewAnimThink( void )
{
// Check for the flag that will reset our view animations
// when the player respawns
if ( m_bForceProneChange )
{
ResetViewOffsetAnimation();
m_pOuter->SetViewOffset( VEC_VIEW_SCALED( m_pOuter ) );
m_bForceProneChange = false;
}
if ( m_pViewOffsetAnim )
{
m_pViewOffsetAnim->Think();
}
}
void CDODPlayerShared::ComputeWorldSpaceSurroundingBox( Vector *pVecWorldMins, Vector *pVecWorldMaxs )
{
Vector org = m_pOuter->GetAbsOrigin();
if ( IsProne() )
{
static Vector vecProneMin(-44, -44, 0 );
static Vector vecProneMax(44, 44, 24 );
VectorAdd( vecProneMin, org, *pVecWorldMins );
VectorAdd( vecProneMax, org, *pVecWorldMaxs );
}
else
{
static Vector vecMin(-32, -32, 0 );
static Vector vecMax(32, 32, 72 );
VectorAdd( vecMin, org, *pVecWorldMins );
VectorAdd( vecMax, org, *pVecWorldMaxs );
}
}
//-----------------------------------------------------------------------------
// Purpose: Sets whether this player is dominating the specified other player
//-----------------------------------------------------------------------------
void CDODPlayerShared::SetPlayerDominated( CDODPlayer *pPlayer, bool bDominated )
{
int iPlayerIndex = pPlayer->entindex();
m_bPlayerDominated.Set( iPlayerIndex, bDominated );
pPlayer->m_Shared.SetPlayerDominatingMe( m_pOuter, bDominated );
}
//-----------------------------------------------------------------------------
// Purpose: Sets whether this player is being dominated by the other player
//-----------------------------------------------------------------------------
void CDODPlayerShared::SetPlayerDominatingMe( CDODPlayer *pPlayer, bool bDominated )
{
int iPlayerIndex = pPlayer->entindex();
m_bPlayerDominatingMe.Set( iPlayerIndex, bDominated );
}
//-----------------------------------------------------------------------------
// Purpose: Returns whether this player is dominating the specified other player
//-----------------------------------------------------------------------------
bool CDODPlayerShared::IsPlayerDominated( int iPlayerIndex )
{
#ifdef CLIENT_DLL
// On the client, we only have data for the local player.
// As a result, it's only valid to ask for dominations related to the local player
C_DODPlayer *pLocalPlayer = C_DODPlayer::GetLocalDODPlayer();
if ( !pLocalPlayer )
return false;
Assert( m_pOuter->IsLocalPlayer() || pLocalPlayer->entindex() == iPlayerIndex );
if ( m_pOuter->IsLocalPlayer() )
return m_bPlayerDominated.Get( iPlayerIndex );
return pLocalPlayer->m_Shared.IsPlayerDominatingMe( m_pOuter->entindex() );
#else
// Server has all the data.
return m_bPlayerDominated.Get( iPlayerIndex );
#endif
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CDODPlayerShared::IsPlayerDominatingMe( int iPlayerIndex )
{
return m_bPlayerDominatingMe.Get( iPlayerIndex );
}