|
|
//========= 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 ); }
|