You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
370 lines
9.3 KiB
370 lines
9.3 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
//=============================================================================// |
|
|
|
#include "cbase.h" |
|
#include "fx_dod_shared.h" |
|
#include "weapon_dodbase.h" |
|
#include "engine/ivdebugoverlay.h" |
|
|
|
#ifndef CLIENT_DLL |
|
#include "ilagcompensationmanager.h" |
|
#endif |
|
|
|
#ifndef CLIENT_DLL |
|
|
|
//============================================================================= |
|
// |
|
// Explosions. |
|
// |
|
class CTEDODExplosion : public CBaseTempEntity |
|
{ |
|
public: |
|
|
|
DECLARE_CLASS( CTEDODExplosion, CBaseTempEntity ); |
|
DECLARE_SERVERCLASS(); |
|
|
|
CTEDODExplosion( const char *name ); |
|
|
|
public: |
|
|
|
Vector m_vecOrigin; |
|
Vector m_vecNormal; |
|
}; |
|
|
|
// Singleton to fire explosion objects |
|
static CTEDODExplosion g_TEDODExplosion( "DODExplosion" ); |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *name - |
|
//----------------------------------------------------------------------------- |
|
CTEDODExplosion::CTEDODExplosion( const char *name ) : CBaseTempEntity( name ) |
|
{ |
|
m_vecOrigin.Init(); |
|
m_vecNormal.Init(); |
|
} |
|
|
|
IMPLEMENT_SERVERCLASS_ST( CTEDODExplosion, DT_TEDODExplosion ) |
|
SendPropFloat( SENDINFO_NOCHECK( m_vecOrigin[0] ), -1, SPROP_COORD_MP_INTEGRAL ), |
|
SendPropFloat( SENDINFO_NOCHECK( m_vecOrigin[1] ), -1, SPROP_COORD_MP_INTEGRAL ), |
|
SendPropFloat( SENDINFO_NOCHECK( m_vecOrigin[2] ), -1, SPROP_COORD_MP_INTEGRAL ), |
|
SendPropVector( SENDINFO_NOCHECK( m_vecNormal ), 6, 0, -1.0f, 1.0f ), |
|
END_SEND_TABLE() |
|
|
|
void TE_DODExplosion( IRecipientFilter &filter, float flDelay, const Vector &vecOrigin, const Vector &vecNormal ) |
|
{ |
|
VectorCopy( vecOrigin, g_TEDODExplosion.m_vecOrigin ); |
|
VectorCopy( vecNormal, g_TEDODExplosion.m_vecNormal ); |
|
|
|
// Send it over the wire |
|
g_TEDODExplosion.Create( filter, flDelay ); |
|
} |
|
|
|
#endif |
|
|
|
#ifdef CLIENT_DLL |
|
|
|
#include "fx_impact.h" |
|
|
|
extern void FX_TracerSound( const Vector &start, const Vector &end, int iTracerType ); |
|
|
|
// this is a cheap ripoff from CBaseCombatWeapon::WeaponSound(): |
|
void FX_WeaponSound( |
|
int iPlayerIndex, |
|
WeaponSound_t sound_type, |
|
const Vector &vOrigin, |
|
CDODWeaponInfo *pWeaponInfo ) |
|
{ |
|
|
|
// If we have some sounds from the weapon classname.txt file, play a random one of them |
|
const char *shootsound = pWeaponInfo->aShootSounds[ sound_type ]; |
|
if ( !shootsound || !shootsound[0] ) |
|
return; |
|
|
|
CBroadcastRecipientFilter filter; // this is client side only |
|
|
|
if ( !te->CanPredict() ) |
|
return; |
|
|
|
CBaseEntity::EmitSound( filter, iPlayerIndex, shootsound, &vOrigin ); |
|
} |
|
|
|
class CGroupedSound |
|
{ |
|
public: |
|
string_t m_SoundName; |
|
Vector m_vPos; |
|
}; |
|
|
|
CUtlVector<CGroupedSound> g_GroupedSounds; |
|
|
|
|
|
// Called by the ImpactSound function. |
|
void ShotgunImpactSoundGroup( const char *pSoundName, const Vector &vEndPos ) |
|
{ |
|
// Don't play the sound if it's too close to another impact sound. |
|
for ( int i=0; i < g_GroupedSounds.Count(); i++ ) |
|
{ |
|
CGroupedSound *pSound = &g_GroupedSounds[i]; |
|
|
|
if ( vEndPos.DistToSqr( pSound->m_vPos ) < 300*300 ) |
|
{ |
|
if ( Q_stricmp( pSound->m_SoundName, pSoundName ) == 0 ) |
|
return; |
|
} |
|
} |
|
|
|
// Ok, play the sound and add it to the list. |
|
CLocalPlayerFilter filter; |
|
C_BaseEntity::EmitSound( filter, NULL, pSoundName, &vEndPos ); |
|
|
|
int tail = g_GroupedSounds.AddToTail(); |
|
g_GroupedSounds[tail].m_SoundName = pSoundName; |
|
g_GroupedSounds[tail].m_vPos = vEndPos; |
|
} |
|
|
|
|
|
void StartGroupingSounds() |
|
{ |
|
Assert( g_GroupedSounds.Count() == 0 ); |
|
SetImpactSoundRoute( ShotgunImpactSoundGroup ); |
|
} |
|
|
|
|
|
void EndGroupingSounds() |
|
{ |
|
g_GroupedSounds.Purge(); |
|
SetImpactSoundRoute( NULL ); |
|
} |
|
|
|
#else |
|
|
|
#include "te_firebullets.h" |
|
|
|
// Server doesn't play sounds anyway. |
|
void StartGroupingSounds() {} |
|
void EndGroupingSounds() {} |
|
void FX_WeaponSound ( int iPlayerIndex, |
|
WeaponSound_t sound_type, |
|
const Vector &vOrigin, |
|
CDODWeaponInfo *pWeaponInfo ) {}; |
|
|
|
#endif |
|
|
|
|
|
|
|
// This runs on both the client and the server. |
|
// On the server, it only does the damage calculations. |
|
// On the client, it does all the effects. |
|
void FX_FireBullets( |
|
int iPlayerIndex, |
|
const Vector &vOrigin, |
|
const QAngle &vAngles, |
|
int iWeaponID, |
|
int iMode, |
|
int iSeed, |
|
float flSpread |
|
) |
|
{ |
|
bool bDoEffects = true; |
|
|
|
#ifdef CLIENT_DLL |
|
C_DODPlayer *pPlayer = ToDODPlayer( ClientEntityList().GetBaseEntity( iPlayerIndex ) ); |
|
#else |
|
CDODPlayer *pPlayer = ToDODPlayer( UTIL_PlayerByIndex( iPlayerIndex) ); |
|
#endif |
|
|
|
const char * weaponAlias = WeaponIDToAlias( iWeaponID ); |
|
|
|
if ( !weaponAlias ) |
|
{ |
|
DevMsg("FX_FireBullets: weapon alias for ID %i not found\n", iWeaponID ); |
|
return; |
|
} |
|
|
|
//MATTTODO: Why are we looking up the weapon info again when every weapon |
|
// stores its own m_pWeaponInfo pointer? |
|
|
|
char wpnName[128]; |
|
Q_snprintf( wpnName, sizeof( wpnName ), "weapon_%s", weaponAlias ); |
|
WEAPON_FILE_INFO_HANDLE hWpnInfo = LookupWeaponInfoSlot( wpnName ); |
|
|
|
if ( hWpnInfo == GetInvalidWeaponInfoHandle() ) |
|
{ |
|
DevMsg("FX_FireBullets: LookupWeaponInfoSlot failed for weapon %s\n", wpnName ); |
|
return; |
|
} |
|
|
|
CDODWeaponInfo *pWeaponInfo = static_cast< CDODWeaponInfo* >( GetFileWeaponInfoFromHandle( hWpnInfo ) ); |
|
|
|
#ifdef CLIENT_DLL |
|
if( pPlayer && !pPlayer->IsDormant() ) |
|
pPlayer->DoAnimationEvent( PLAYERANIMEVENT_FIRE_GUN ); |
|
#else |
|
if( pPlayer ) |
|
pPlayer->DoAnimationEvent( PLAYERANIMEVENT_FIRE_GUN ); |
|
#endif |
|
|
|
#ifndef CLIENT_DLL |
|
// if this is server code, send the effect over to client as temp entity |
|
// Dispatch one message for all the bullet impacts and sounds. |
|
TE_FireBullets( |
|
iPlayerIndex, |
|
vOrigin, |
|
vAngles, |
|
iWeaponID, |
|
iMode, |
|
iSeed, |
|
flSpread |
|
); |
|
|
|
bDoEffects = false; // no effects on server |
|
|
|
// Let the player remember the usercmd he fired a weapon on. Assists in making decisions about lag compensation. |
|
pPlayer->NoteWeaponFired(); |
|
#endif |
|
|
|
|
|
|
|
WeaponSound_t sound_type = SINGLE; |
|
|
|
if ( bDoEffects) |
|
{ |
|
FX_WeaponSound( iPlayerIndex, sound_type, vOrigin, pWeaponInfo ); |
|
} |
|
|
|
// Fire bullets, calculate impacts & effects |
|
if ( !pPlayer ) |
|
return; |
|
|
|
StartGroupingSounds(); |
|
|
|
#if !defined (CLIENT_DLL) |
|
// Move other players back to history positions based on local player's lag |
|
lagcompensation->StartLagCompensation( pPlayer, pPlayer->GetCurrentCommand() ); |
|
#endif |
|
|
|
RandomSeed( iSeed ); |
|
|
|
float x, y; |
|
do |
|
{ |
|
x = random->RandomFloat( -0.5, 0.5 ) + random->RandomFloat( -0.5, 0.5 ); |
|
y = random->RandomFloat( -0.5, 0.5 ) + random->RandomFloat( -0.5, 0.5 ); |
|
} while ( (x * x + y * y) > 1.0f ); |
|
|
|
Vector vecForward, vecRight, vecUp; |
|
AngleVectors( vAngles, &vecForward, &vecRight, &vecUp ); |
|
|
|
Vector vecDirShooting = vecForward + |
|
x * flSpread * vecRight + |
|
y * flSpread * vecUp; |
|
|
|
vecDirShooting.NormalizeInPlace(); |
|
|
|
FireBulletsInfo_t info( 1 /*shots*/, vOrigin, vecDirShooting, Vector( flSpread, flSpread, FLOAT32_NAN), MAX_COORD_RANGE, pWeaponInfo->iAmmoType ); |
|
info.m_flDamage = pWeaponInfo->m_iDamage; |
|
info.m_pAttacker = pPlayer; |
|
|
|
pPlayer->FireBullets( info ); |
|
|
|
#ifdef CLIENT_DLL |
|
|
|
{ |
|
trace_t tr; |
|
UTIL_TraceLine( vOrigin, vOrigin + vecDirShooting * MAX_COORD_RANGE, MASK_SOLID, pPlayer, COLLISION_GROUP_NONE, &tr ); |
|
|
|
// if this is a local player, start at attachment on view model |
|
// else start on attachment on weapon model |
|
|
|
int iEntIndex = pPlayer->entindex(); |
|
int iAttachment = 1; |
|
|
|
Vector vecStart = tr.startpos; |
|
QAngle angAttachment; |
|
|
|
C_DODPlayer *pLocalPlayer = C_DODPlayer::GetLocalDODPlayer(); |
|
|
|
bool bInToolRecordingMode = clienttools->IsInRecordingMode(); |
|
|
|
// try to align tracers to actual weapon barrel if possible |
|
if ( pPlayer->IsLocalPlayer() && !bInToolRecordingMode ) |
|
{ |
|
C_BaseViewModel *pViewModel = pPlayer->GetViewModel(0); |
|
|
|
if ( pViewModel ) |
|
{ |
|
iEntIndex = pViewModel->entindex(); |
|
pViewModel->GetAttachment( iAttachment, vecStart, angAttachment ); |
|
} |
|
} |
|
else if ( pLocalPlayer && |
|
pLocalPlayer->GetObserverTarget() == pPlayer && |
|
pLocalPlayer->GetObserverMode() == OBS_MODE_IN_EYE ) |
|
{ |
|
// get our observer target's view model |
|
|
|
C_BaseViewModel *pViewModel = pLocalPlayer->GetViewModel(0); |
|
|
|
if ( pViewModel ) |
|
{ |
|
iEntIndex = pViewModel->entindex(); |
|
pViewModel->GetAttachment( iAttachment, vecStart, angAttachment ); |
|
} |
|
} |
|
else if ( !pPlayer->IsDormant() ) |
|
{ |
|
// fill in with third person weapon model index |
|
C_BaseCombatWeapon *pWeapon = pPlayer->GetActiveWeapon(); |
|
|
|
if( pWeapon ) |
|
{ |
|
iEntIndex = pWeapon->entindex(); |
|
|
|
int nModelIndex = pWeapon->GetModelIndex(); |
|
int nWorldModelIndex = pWeapon->GetWorldModelIndex(); |
|
if ( bInToolRecordingMode && nModelIndex != nWorldModelIndex ) |
|
{ |
|
pWeapon->SetModelIndex( nWorldModelIndex ); |
|
} |
|
|
|
pWeapon->GetAttachment( iAttachment, vecStart, angAttachment ); |
|
|
|
if ( bInToolRecordingMode && nModelIndex != nWorldModelIndex ) |
|
{ |
|
pWeapon->SetModelIndex( nModelIndex ); |
|
} |
|
} |
|
} |
|
|
|
switch( pWeaponInfo->m_iTracerType ) |
|
{ |
|
case 1: // Machine gun, heavy tracer |
|
UTIL_Tracer( vecStart, tr.endpos, iEntIndex, TRACER_DONT_USE_ATTACHMENT, 5000.0, true, "BrightTracer" ); |
|
break; |
|
|
|
case 2: // rifle, smg, light tracer |
|
vecStart += vecDirShooting * 150; |
|
UTIL_Tracer( vecStart, tr.endpos, iEntIndex, TRACER_DONT_USE_ATTACHMENT, 5000.0, true, "FaintTracer" ); |
|
break; |
|
|
|
case 0: // pistols etc, just do the sound |
|
{ |
|
FX_TracerSound( vecStart, tr.endpos, TRACER_TYPE_DEFAULT ); |
|
} |
|
default: |
|
break; |
|
} |
|
} |
|
#endif |
|
|
|
#if !defined (CLIENT_DLL) |
|
lagcompensation->FinishLagCompensation( pPlayer ); |
|
#endif |
|
|
|
EndGroupingSounds(); |
|
} |
|
|
|
|