mirror of
https://github.com/nillerusr/source-engine.git
synced 2025-04-02 05:22:34 +00:00
580 lines
16 KiB
C++
580 lines
16 KiB
C++
//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. =======
|
|
//
|
|
// Purpose: Weapon Base Gun
|
|
//
|
|
//=============================================================================
|
|
|
|
#include "cbase.h"
|
|
#include "tf_weaponbase_gun.h"
|
|
#include "tf_fx_shared.h"
|
|
#include "effect_dispatch_data.h"
|
|
#include "takedamageinfo.h"
|
|
#include "tf_projectile_nail.h"
|
|
|
|
#if !defined( CLIENT_DLL ) // Server specific.
|
|
|
|
#include "tf_gamestats.h"
|
|
#include "tf_player.h"
|
|
#include "tf_fx.h"
|
|
#include "te_effect_dispatch.h"
|
|
|
|
#include "tf_projectile_rocket.h"
|
|
#include "tf_weapon_grenade_pipebomb.h"
|
|
#include "te.h"
|
|
|
|
#else // Client specific.
|
|
|
|
#include "c_tf_player.h"
|
|
#include "c_te_effect_dispatch.h"
|
|
|
|
#endif
|
|
|
|
//=============================================================================
|
|
//
|
|
// TFWeaponBase Gun tables.
|
|
//
|
|
IMPLEMENT_NETWORKCLASS_ALIASED( TFWeaponBaseGun, DT_TFWeaponBaseGun )
|
|
|
|
BEGIN_NETWORK_TABLE( CTFWeaponBaseGun, DT_TFWeaponBaseGun )
|
|
END_NETWORK_TABLE()
|
|
|
|
BEGIN_PREDICTION_DATA( CTFWeaponBaseGun )
|
|
END_PREDICTION_DATA()
|
|
|
|
// Server specific.
|
|
#if !defined( CLIENT_DLL )
|
|
BEGIN_DATADESC( CTFWeaponBaseGun )
|
|
DEFINE_THINKFUNC( ZoomOutIn ),
|
|
DEFINE_THINKFUNC( ZoomOut ),
|
|
DEFINE_THINKFUNC( ZoomIn ),
|
|
END_DATADESC()
|
|
#endif
|
|
|
|
//=============================================================================
|
|
//
|
|
// TFWeaponBase Gun functions.
|
|
//
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Constructor.
|
|
//-----------------------------------------------------------------------------
|
|
CTFWeaponBaseGun::CTFWeaponBaseGun()
|
|
{
|
|
m_iWeaponMode = TF_WEAPON_PRIMARY_MODE;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CTFWeaponBaseGun::PrimaryAttack( void )
|
|
{
|
|
// Check for ammunition.
|
|
if ( m_iClip1 <= 0 && m_iClip1 != -1 )
|
|
return;
|
|
|
|
// Are we capable of firing again?
|
|
if ( m_flNextPrimaryAttack > gpGlobals->curtime )
|
|
return;
|
|
|
|
// Get the player owning the weapon.
|
|
CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
|
|
if ( !pPlayer )
|
|
return;
|
|
|
|
if ( !CanAttack() )
|
|
return;
|
|
|
|
CalcIsAttackCritical();
|
|
|
|
#ifndef CLIENT_DLL
|
|
pPlayer->RemoveInvisibility();
|
|
pPlayer->RemoveDisguise();
|
|
|
|
// Minigun has custom handling
|
|
if ( GetWeaponID() != TF_WEAPON_MINIGUN )
|
|
{
|
|
pPlayer->SpeakWeaponFire();
|
|
}
|
|
CTF_GameStats.Event_PlayerFiredWeapon( pPlayer, IsCurrentAttackACrit() );
|
|
#endif
|
|
|
|
// Set the weapon mode.
|
|
m_iWeaponMode = TF_WEAPON_PRIMARY_MODE;
|
|
|
|
SendWeaponAnim( ACT_VM_PRIMARYATTACK );
|
|
|
|
pPlayer->SetAnimation( PLAYER_ATTACK1 );
|
|
|
|
FireProjectile( pPlayer );
|
|
|
|
// Set next attack times.
|
|
m_flNextPrimaryAttack = gpGlobals->curtime + m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_flTimeFireDelay;
|
|
|
|
// Don't push out secondary attack, because our secondary fire
|
|
// systems are all separate from primary fire (sniper zooming, demoman pipebomb detonating, etc)
|
|
//m_flNextSecondaryAttack = gpGlobals->curtime + m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_flTimeFireDelay;
|
|
|
|
// Set the idle animation times based on the sequence duration, so that we play full fire animations
|
|
// that last longer than the refire rate may allow.
|
|
if ( Clip1() > 0 )
|
|
{
|
|
SetWeaponIdleTime( gpGlobals->curtime + SequenceDuration() );
|
|
}
|
|
else
|
|
{
|
|
SetWeaponIdleTime( gpGlobals->curtime + SequenceDuration() );
|
|
}
|
|
|
|
// Check the reload mode and behave appropriately.
|
|
if ( m_bReloadsSingly )
|
|
{
|
|
m_iReloadMode.Set( TF_RELOAD_START );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CTFWeaponBaseGun::SecondaryAttack( void )
|
|
{
|
|
// semi-auto behaviour
|
|
if ( m_bInAttack2 )
|
|
return;
|
|
|
|
// Get the player owning the weapon.
|
|
CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
|
|
if ( !pPlayer )
|
|
return;
|
|
|
|
pPlayer->DoClassSpecialSkill();
|
|
|
|
m_bInAttack2 = true;
|
|
|
|
m_flNextSecondaryAttack = gpGlobals->curtime + 0.5;
|
|
}
|
|
|
|
CBaseEntity *CTFWeaponBaseGun::FireProjectile( CTFPlayer *pPlayer )
|
|
{
|
|
int iProjectile = m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_iProjectile;
|
|
|
|
CBaseEntity *pProjectile = NULL;
|
|
|
|
switch( iProjectile )
|
|
{
|
|
case TF_PROJECTILE_BULLET:
|
|
FireBullet( pPlayer );
|
|
break;
|
|
|
|
case TF_PROJECTILE_ROCKET:
|
|
pProjectile = FireRocket( pPlayer );
|
|
pPlayer->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_PRIMARY );
|
|
break;
|
|
|
|
case TF_PROJECTILE_SYRINGE:
|
|
pProjectile = FireNail( pPlayer, iProjectile );
|
|
pPlayer->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_PRIMARY );
|
|
break;
|
|
|
|
case TF_PROJECTILE_PIPEBOMB:
|
|
pProjectile = FirePipeBomb( pPlayer, false );
|
|
pPlayer->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_PRIMARY );
|
|
break;
|
|
|
|
case TF_PROJECTILE_PIPEBOMB_REMOTE:
|
|
pProjectile = FirePipeBomb( pPlayer, true );
|
|
pPlayer->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_PRIMARY );
|
|
break;
|
|
|
|
case TF_PROJECTILE_NONE:
|
|
default:
|
|
// do nothing!
|
|
DevMsg( "Weapon does not have a projectile type set\n" );
|
|
break;
|
|
}
|
|
|
|
if ( m_iClip1 != -1 )
|
|
{
|
|
m_iClip1 -= m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_iAmmoPerShot;
|
|
}
|
|
else
|
|
{
|
|
if ( m_iWeaponMode == TF_WEAPON_PRIMARY_MODE )
|
|
{
|
|
pPlayer->RemoveAmmo( m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_iAmmoPerShot, m_iPrimaryAmmoType );
|
|
}
|
|
else
|
|
{
|
|
pPlayer->RemoveAmmo( m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_iAmmoPerShot, m_iSecondaryAmmoType );
|
|
}
|
|
}
|
|
|
|
DoFireEffects();
|
|
|
|
UpdatePunchAngles( pPlayer );
|
|
|
|
return pProjectile;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CTFWeaponBaseGun::UpdatePunchAngles( CTFPlayer *pPlayer )
|
|
{
|
|
// Update the player's punch angle.
|
|
QAngle angle = pPlayer->GetPunchAngle();
|
|
float flPunchAngle = m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_flPunchAngle;
|
|
|
|
if ( flPunchAngle > 0 )
|
|
{
|
|
angle.x -= SharedRandomInt( "ShotgunPunchAngle", ( flPunchAngle - 1 ), ( flPunchAngle + 1 ) );
|
|
pPlayer->SetPunchAngle( angle );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Fire a bullet!
|
|
//-----------------------------------------------------------------------------
|
|
void CTFWeaponBaseGun::FireBullet( CTFPlayer *pPlayer )
|
|
{
|
|
PlayWeaponShootSound();
|
|
|
|
FX_FireBullets(
|
|
pPlayer->entindex(),
|
|
pPlayer->Weapon_ShootPosition(),
|
|
pPlayer->EyeAngles() + pPlayer->GetPunchAngle(),
|
|
GetWeaponID(),
|
|
m_iWeaponMode,
|
|
CBaseEntity::GetPredictionRandomSeed() & 255,
|
|
GetWeaponSpread(),
|
|
GetProjectileDamage(),
|
|
IsCurrentAttackACrit() );
|
|
}
|
|
|
|
class CTraceFilterIgnoreTeammates : public CTraceFilterSimple
|
|
{
|
|
public:
|
|
// It does have a base, but we'll never network anything below here..
|
|
DECLARE_CLASS( CTraceFilterIgnoreTeammates, CTraceFilterSimple );
|
|
|
|
CTraceFilterIgnoreTeammates( const IHandleEntity *passentity, int collisionGroup, int iIgnoreTeam )
|
|
: CTraceFilterSimple( passentity, collisionGroup ), m_iIgnoreTeam( iIgnoreTeam )
|
|
{
|
|
}
|
|
|
|
virtual bool ShouldHitEntity( IHandleEntity *pServerEntity, int contentsMask )
|
|
{
|
|
CBaseEntity *pEntity = EntityFromEntityHandle( pServerEntity );
|
|
|
|
if ( pEntity->IsPlayer() && pEntity->GetTeamNumber() == m_iIgnoreTeam )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
int m_iIgnoreTeam;
|
|
};
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Return the origin & angles for a projectile fired from the player's gun
|
|
//-----------------------------------------------------------------------------
|
|
void CTFWeaponBaseGun::GetProjectileFireSetup( CTFPlayer *pPlayer, Vector vecOffset, Vector *vecSrc, QAngle *angForward, bool bHitTeammates /* = true */ )
|
|
{
|
|
Vector vecForward, vecRight, vecUp;
|
|
AngleVectors( pPlayer->EyeAngles(), &vecForward, &vecRight, &vecUp );
|
|
|
|
Vector vecShootPos = pPlayer->Weapon_ShootPosition();
|
|
|
|
// Estimate end point
|
|
Vector endPos = vecShootPos + vecForward * 2000;
|
|
|
|
// Trace forward and find what's in front of us, and aim at that
|
|
trace_t tr;
|
|
|
|
if ( bHitTeammates )
|
|
{
|
|
CTraceFilterSimple filter( pPlayer, COLLISION_GROUP_NONE );
|
|
UTIL_TraceLine( vecShootPos, endPos, MASK_SOLID, &filter, &tr );
|
|
}
|
|
else
|
|
{
|
|
CTraceFilterIgnoreTeammates filter( pPlayer, COLLISION_GROUP_NONE, pPlayer->GetTeamNumber() );
|
|
UTIL_TraceLine( vecShootPos, endPos, MASK_SOLID, &filter, &tr );
|
|
}
|
|
|
|
// Offset actual start point
|
|
*vecSrc = vecShootPos + (vecForward * vecOffset.x) + (vecRight * vecOffset.y) + (vecUp * vecOffset.z);
|
|
|
|
// Find angles that will get us to our desired end point
|
|
// Only use the trace end if it wasn't too close, which results
|
|
// in visually bizarre forward angles
|
|
if ( tr.fraction > 0.1 )
|
|
{
|
|
VectorAngles( tr.endpos - *vecSrc, *angForward );
|
|
}
|
|
else
|
|
{
|
|
VectorAngles( endPos - *vecSrc, *angForward );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Fire a rocket
|
|
//-----------------------------------------------------------------------------
|
|
CBaseEntity *CTFWeaponBaseGun::FireRocket( CTFPlayer *pPlayer )
|
|
{
|
|
PlayWeaponShootSound();
|
|
|
|
// Server only - create the rocket.
|
|
#ifdef GAME_DLL
|
|
|
|
Vector vecSrc;
|
|
QAngle angForward;
|
|
Vector vecOffset( 23.5f, 12.0f, -3.0f );
|
|
if ( pPlayer->GetFlags() & FL_DUCKING )
|
|
{
|
|
vecOffset.z = 8.0f;
|
|
}
|
|
GetProjectileFireSetup( pPlayer, vecOffset, &vecSrc, &angForward, false );
|
|
|
|
CTFProjectile_Rocket *pProjectile = CTFProjectile_Rocket::Create( vecSrc, angForward, pPlayer, pPlayer );
|
|
if ( pProjectile )
|
|
{
|
|
pProjectile->SetCritical( IsCurrentAttackACrit() );
|
|
pProjectile->SetDamage( GetProjectileDamage() );
|
|
}
|
|
return pProjectile;
|
|
|
|
#endif
|
|
|
|
return NULL;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Fire a projectile nail
|
|
//-----------------------------------------------------------------------------
|
|
CBaseEntity *CTFWeaponBaseGun::FireNail( CTFPlayer *pPlayer, int iSpecificNail )
|
|
{
|
|
PlayWeaponShootSound();
|
|
|
|
Vector vecSrc;
|
|
QAngle angForward;
|
|
GetProjectileFireSetup( pPlayer, Vector(16,6,-8), &vecSrc, &angForward );
|
|
|
|
// Add some spread
|
|
float flSpread = 1.5;
|
|
angForward.x += RandomFloat( -flSpread, flSpread );
|
|
angForward.y += RandomFloat( -flSpread, flSpread );
|
|
|
|
CTFBaseProjectile *pProjectile = NULL;
|
|
switch( iSpecificNail )
|
|
{
|
|
case TF_PROJECTILE_SYRINGE:
|
|
pProjectile = CTFProjectile_Syringe::Create( vecSrc, angForward, pPlayer, pPlayer, IsCurrentAttackACrit() );
|
|
break;
|
|
|
|
default:
|
|
Assert(0);
|
|
}
|
|
|
|
if ( pProjectile )
|
|
{
|
|
pProjectile->SetWeaponID( GetWeaponID() );
|
|
pProjectile->SetCritical( IsCurrentAttackACrit() );
|
|
#ifdef GAME_DLL
|
|
pProjectile->SetDamage( GetProjectileDamage() );
|
|
#endif
|
|
}
|
|
|
|
return pProjectile;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Fire a pipe bomb
|
|
//-----------------------------------------------------------------------------
|
|
CBaseEntity *CTFWeaponBaseGun::FirePipeBomb( CTFPlayer *pPlayer, bool bRemoteDetonate )
|
|
{
|
|
PlayWeaponShootSound();
|
|
|
|
#ifdef GAME_DLL
|
|
|
|
Vector vecForward, vecRight, vecUp;
|
|
AngleVectors( pPlayer->EyeAngles(), &vecForward, &vecRight, &vecUp );
|
|
|
|
// Create grenades here!!
|
|
Vector vecSrc = pPlayer->Weapon_ShootPosition();
|
|
vecSrc += vecForward * 16.0f + vecRight * 8.0f + vecUp * -6.0f;
|
|
|
|
Vector vecVelocity = ( vecForward * GetProjectileSpeed() ) + ( vecUp * 200.0f ) + ( random->RandomFloat( -10.0f, 10.0f ) * vecRight ) +
|
|
( random->RandomFloat( -10.0f, 10.0f ) * vecUp );
|
|
|
|
CTFGrenadePipebombProjectile *pProjectile = CTFGrenadePipebombProjectile::Create( vecSrc, pPlayer->EyeAngles(), vecVelocity,
|
|
AngularImpulse( 600, random->RandomInt( -1200, 1200 ), 0 ),
|
|
pPlayer, GetTFWpnData(), bRemoteDetonate );
|
|
|
|
|
|
if ( pProjectile )
|
|
{
|
|
pProjectile->SetCritical( IsCurrentAttackACrit() );
|
|
}
|
|
return pProjectile;
|
|
|
|
#endif
|
|
|
|
return NULL;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CTFWeaponBaseGun::PlayWeaponShootSound( void )
|
|
{
|
|
if ( IsCurrentAttackACrit() )
|
|
{
|
|
WeaponSound( BURST );
|
|
}
|
|
else
|
|
{
|
|
WeaponSound( SINGLE );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Accessor for damage, so sniper etc can modify damage
|
|
//-----------------------------------------------------------------------------
|
|
float CTFWeaponBaseGun::GetProjectileSpeed( void )
|
|
{
|
|
// placeholder for now
|
|
// grenade launcher and pipebomb launcher hook this to set variable pipebomb speed
|
|
|
|
return 0;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
float CTFWeaponBaseGun::GetWeaponSpread( void )
|
|
{
|
|
return m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_flSpread;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Accessor for damage, so sniper etc can modify damage
|
|
//-----------------------------------------------------------------------------
|
|
float CTFWeaponBaseGun::GetProjectileDamage( void )
|
|
{
|
|
return (float)m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_nDamage;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
bool CTFWeaponBaseGun::Holster( CBaseCombatWeapon *pSwitchingTo )
|
|
{
|
|
// Server specific.
|
|
#if !defined( CLIENT_DLL )
|
|
|
|
// Make sure to zoom out before we holster the weapon.
|
|
ZoomOut();
|
|
SetContextThink( NULL, 0, ZOOM_CONTEXT );
|
|
|
|
#endif
|
|
|
|
return BaseClass::Holster( pSwitchingTo );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// NOTE: Should this be put into fire gun
|
|
//-----------------------------------------------------------------------------
|
|
void CTFWeaponBaseGun::DoFireEffects()
|
|
{
|
|
CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
|
|
if ( !pPlayer )
|
|
return;
|
|
|
|
// Muzzle flash on weapon.
|
|
bool bMuzzleFlash = true;
|
|
if ( pPlayer->IsPlayerClass( TF_CLASS_HEAVYWEAPONS ) )
|
|
{
|
|
CTFWeaponBase *pWeapon = pPlayer->GetActiveTFWeapon();
|
|
if ( pWeapon && pWeapon->GetWeaponID() == TF_WEAPON_MINIGUN )
|
|
{
|
|
bMuzzleFlash = false;
|
|
}
|
|
}
|
|
|
|
if ( bMuzzleFlash )
|
|
{
|
|
pPlayer->DoMuzzleFlash();
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CTFWeaponBaseGun::ToggleZoom( void )
|
|
{
|
|
// Toggle the zoom.
|
|
CBasePlayer *pPlayer = GetPlayerOwner();
|
|
if ( pPlayer )
|
|
{
|
|
if( pPlayer->GetFOV() >= 75 )
|
|
{
|
|
ZoomIn();
|
|
}
|
|
else
|
|
{
|
|
ZoomOut();
|
|
}
|
|
}
|
|
|
|
// Get the zoom animation time.
|
|
m_flNextSecondaryAttack = gpGlobals->curtime + 1.2;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CTFWeaponBaseGun::ZoomIn( void )
|
|
{
|
|
// The the owning player.
|
|
CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
|
|
if ( !pPlayer )
|
|
return;
|
|
|
|
// Set the weapon zoom.
|
|
// TODO: The weapon fov should be gotten from the script file.
|
|
pPlayer->SetFOV( pPlayer, TF_WEAPON_ZOOM_FOV, 0.1f );
|
|
pPlayer->m_Shared.AddCond( TF_COND_ZOOMED );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CTFWeaponBaseGun::ZoomOut( void )
|
|
{
|
|
// The the owning player.
|
|
CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
|
|
if ( !pPlayer )
|
|
return;
|
|
|
|
if ( pPlayer->m_Shared.InCond( TF_COND_ZOOMED ) )
|
|
{
|
|
// Set the FOV to 0 set the default FOV.
|
|
pPlayer->SetFOV( pPlayer, 0, 0.1f );
|
|
pPlayer->m_Shared.RemoveCond( TF_COND_ZOOMED );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CTFWeaponBaseGun::ZoomOutIn( void )
|
|
{
|
|
//Zoom out, set think to zoom back in.
|
|
ZoomOut();
|
|
SetContextThink( &CTFWeaponBaseGun::ZoomIn, gpGlobals->curtime + ZOOM_REZOOM_TIME, ZOOM_CONTEXT );
|
|
}
|