Team Fortress 2 Source Code as on 22/4/2020
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.
 
 
 
 
 
 

379 lines
10 KiB

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Crowbar - an old favorite
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "hl1mp_basecombatweapon_shared.h"
#ifdef CLIENT_DLL
#include "c_baseplayer.h"
#include "fx_impact.h"
#include "fx.h"
#else
#include "player.h"
#include "soundent.h"
#endif
#include "gamerules.h"
#include "ammodef.h"
#include "mathlib/mathlib.h"
#include "in_buttons.h"
#include "vstdlib/random.h"
extern ConVar sk_plr_dmg_crowbar;
#define CROWBAR_RANGE 64.0f
#define CROWBAR_REFIRE_MISS 0.5f
#define CROWBAR_REFIRE_HIT 0.25f
#ifdef CLIENT_DLL
#define CWeaponCrowbar C_WeaponCrowbar
#endif
//-----------------------------------------------------------------------------
// CWeaponCrowbar
//-----------------------------------------------------------------------------
class CWeaponCrowbar : public CBaseHL1MPCombatWeapon
{
DECLARE_CLASS( CWeaponCrowbar, CBaseHL1MPCombatWeapon );
public:
DECLARE_NETWORKCLASS();
DECLARE_PREDICTABLE();
#ifndef CLIENT_DLL
DECLARE_DATADESC();
#endif
CWeaponCrowbar();
void Precache( void );
virtual void ItemPostFrame( void );
void PrimaryAttack( void );
public:
trace_t m_traceHit;
Activity m_nHitActivity;
private:
virtual void Swing( void );
virtual void Hit( void );
virtual void ImpactEffect( void );
void ImpactSound( CBaseEntity *pHitEntity );
virtual Activity ChooseIntersectionPointAndActivity( trace_t &hitTrace, const Vector &mins, const Vector &maxs, CBasePlayer *pOwner );
public:
};
IMPLEMENT_NETWORKCLASS_ALIASED( WeaponCrowbar, DT_WeaponCrowbar );
BEGIN_NETWORK_TABLE( CWeaponCrowbar, DT_WeaponCrowbar )
/// what
END_NETWORK_TABLE()
BEGIN_PREDICTION_DATA( CWeaponCrowbar )
END_PREDICTION_DATA()
LINK_ENTITY_TO_CLASS( weapon_crowbar, CWeaponCrowbar );
PRECACHE_WEAPON_REGISTER( weapon_crowbar );
#ifndef CLIENT_DLL
BEGIN_DATADESC( CWeaponCrowbar )
// DEFINE_FIELD( m_trLineHit, trace_t ),
// DEFINE_FIELD( m_trHullHit, trace_t ),
// DEFINE_FIELD( m_nHitActivity, FIELD_INTEGER ),
// DEFINE_FIELD( m_traceHit, trace_t ),
// Class CWeaponCrowbar:
// DEFINE_FIELD( m_nHitActivity, FIELD_INTEGER ),
// Function Pointers
DEFINE_FUNCTION( Hit ),
END_DATADESC()
#endif
#define BLUDGEON_HULL_DIM 16
static const Vector g_bludgeonMins(-BLUDGEON_HULL_DIM,-BLUDGEON_HULL_DIM,-BLUDGEON_HULL_DIM);
static const Vector g_bludgeonMaxs(BLUDGEON_HULL_DIM,BLUDGEON_HULL_DIM,BLUDGEON_HULL_DIM);
//-----------------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------------
CWeaponCrowbar::CWeaponCrowbar()
{
m_bFiresUnderwater = true;
}
//-----------------------------------------------------------------------------
// Purpose: Precache the weapon
//-----------------------------------------------------------------------------
void CWeaponCrowbar::Precache( void )
{
//Call base class first
BaseClass::Precache();
}
//------------------------------------------------------------------------------
// Purpose : Update weapon
//------------------------------------------------------------------------------
void CWeaponCrowbar::ItemPostFrame( void )
{
CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
if ( pOwner == NULL )
return;
if ( (pOwner->m_nButtons & IN_ATTACK) && (m_flNextPrimaryAttack <= gpGlobals->curtime) )
{
PrimaryAttack();
}
else
{
WeaponIdle();
return;
}
}
//------------------------------------------------------------------------------
// Purpose :
// Input :
// Output :
//------------------------------------------------------------------------------
void CWeaponCrowbar::PrimaryAttack()
{
Swing();
}
//------------------------------------------------------------------------------
// Purpose: Implement impact function
//------------------------------------------------------------------------------
void CWeaponCrowbar::Hit( void )
{
//Make sound for the AI
#ifndef CLIENT_DLL
CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
CSoundEnt::InsertSound( SOUND_BULLET_IMPACT, m_traceHit.endpos, 400, 0.2f, pPlayer );
CBaseEntity *pHitEntity = m_traceHit.m_pEnt;
//Apply damage to a hit target
if ( pHitEntity != NULL )
{
Vector hitDirection;
pPlayer->EyeVectors( &hitDirection, NULL, NULL );
VectorNormalize( hitDirection );
ClearMultiDamage();
CTakeDamageInfo info( GetOwner(), GetOwner(), sk_plr_dmg_crowbar.GetFloat(), DMG_CLUB );
CalculateMeleeDamageForce( &info, hitDirection, m_traceHit.endpos );
pHitEntity->DispatchTraceAttack( info, hitDirection, &m_traceHit );
ApplyMultiDamage();
// Now hit all triggers along the ray that...
TraceAttackToTriggers( CTakeDamageInfo( GetOwner(), GetOwner(), sk_plr_dmg_crowbar.GetFloat(), DMG_CLUB ), m_traceHit.startpos, m_traceHit.endpos, hitDirection );
//Play an impact sound
ImpactSound( pHitEntity );
}
#endif
//Apply an impact effect
ImpactEffect();
}
//-----------------------------------------------------------------------------
// Purpose: Play the impact sound
// Input : pHitEntity - entity that we hit
// assumes pHitEntity is not null
//-----------------------------------------------------------------------------
void CWeaponCrowbar::ImpactSound( CBaseEntity *pHitEntity )
{
bool bIsWorld = ( pHitEntity->entindex() == 0 );
#ifndef CLIENT_DLL
if ( !bIsWorld )
{
bIsWorld |= pHitEntity->Classify() == CLASS_NONE || pHitEntity->Classify() == CLASS_MACHINE;
}
#endif
if( bIsWorld )
{
WeaponSound( MELEE_HIT_WORLD );
}
else
{
WeaponSound( MELEE_HIT );
}
}
Activity CWeaponCrowbar::ChooseIntersectionPointAndActivity( trace_t &hitTrace, const Vector &mins, const Vector &maxs, CBasePlayer *pOwner )
{
int i, j, k;
float distance;
const float *minmaxs[2] = {mins.Base(), maxs.Base()};
trace_t tmpTrace;
Vector vecHullEnd = hitTrace.endpos;
Vector vecEnd;
distance = 1e6f;
Vector vecSrc = hitTrace.startpos;
vecHullEnd = vecSrc + ((vecHullEnd - vecSrc)*2);
UTIL_TraceLine( vecSrc, vecHullEnd, MASK_SHOT_HULL, pOwner, COLLISION_GROUP_NONE, &tmpTrace );
if ( tmpTrace.fraction == 1.0 )
{
for ( i = 0; i < 2; i++ )
{
for ( j = 0; j < 2; j++ )
{
for ( k = 0; k < 2; k++ )
{
vecEnd.x = vecHullEnd.x + minmaxs[i][0];
vecEnd.y = vecHullEnd.y + minmaxs[j][1];
vecEnd.z = vecHullEnd.z + minmaxs[k][2];
UTIL_TraceLine( vecSrc, vecEnd, MASK_SHOT_HULL, pOwner, COLLISION_GROUP_NONE, &tmpTrace );
if ( tmpTrace.fraction < 1.0 )
{
float thisDistance = (tmpTrace.endpos - vecSrc).Length();
if ( thisDistance < distance )
{
hitTrace = tmpTrace;
distance = thisDistance;
}
}
}
}
}
}
else
{
hitTrace = tmpTrace;
}
return ACT_VM_HITCENTER;
}
#ifdef HL1MP_CLIENT_DLL
//-----------------------------------------------------------------------------
// Purpose: Handle jeep impacts
//-----------------------------------------------------------------------------
void ImpactCrowbarCallback( const CEffectData &data )
{
trace_t tr;
Vector vecOrigin, vecStart, vecShotDir;
int iMaterial, iDamageType, iHitbox;
short nSurfaceProp;
C_BaseEntity *pEntity = ParseImpactData( data, &vecOrigin, &vecStart, &vecShotDir, nSurfaceProp, iMaterial, iDamageType, iHitbox );
bool bIsWorld = ( pEntity->entindex() == 0 );
if ( !pEntity )
{
// This happens for impacts that occur on an object that's then destroyed.
// Clear out the fraction so it uses the server's data
tr.fraction = 1.0;
GetActiveWeapon()->WeaponSound( bIsWorld ? MELEE_HIT_WORLD : MELEE_HIT );
return;
}
// If we hit, perform our custom effects and play the sound
if ( Impact( vecOrigin, vecStart, iMaterial, iDamageType, iHitbox, pEntity, tr ) )
{
// Check for custom effects based on the Decal index
PerformCustomEffects( vecOrigin, tr, vecShotDir, iMaterial, 2 );
}
GetActiveWeapon()->WeaponSound( bIsWorld ? MELEE_HIT_WORLD : MELEE_HIT );
}
DECLARE_CLIENT_EFFECT( "ImpactCrowbar", ImpactCrowbarCallback );
#endif
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponCrowbar::ImpactEffect( void )
{
//FIXME: need new decals
#ifdef HL1MP_CLIENT_DLL
// in hl1mp force the basic crowbar sound
UTIL_ImpactTrace( &m_traceHit, DMG_CLUB, "ImpactCrowbar" );
#else
UTIL_ImpactTrace( &m_traceHit, DMG_CLUB );
#endif
}
//------------------------------------------------------------------------------
// Purpose : Starts the swing of the weapon and determines the animation
//------------------------------------------------------------------------------
void CWeaponCrowbar::Swing( void )
{
// Try a ray
CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
if ( !pOwner )
return;
Vector swingStart = pOwner->Weapon_ShootPosition( );
Vector forward;
pOwner->EyeVectors( &forward, NULL, NULL );
Vector swingEnd = swingStart + forward * CROWBAR_RANGE;
UTIL_TraceLine( swingStart, swingEnd, MASK_SHOT_HULL, pOwner, COLLISION_GROUP_NONE, &m_traceHit );
m_nHitActivity = ACT_VM_HITCENTER;
if ( m_traceHit.fraction == 1.0 )
{
float bludgeonHullRadius = 1.732f * BLUDGEON_HULL_DIM; // hull is +/- 16, so use cuberoot of 2 to determine how big the hull is from center to the corner point
// Back off by hull "radius"
swingEnd -= forward * bludgeonHullRadius;
UTIL_TraceHull( swingStart, swingEnd, g_bludgeonMins, g_bludgeonMaxs, MASK_SHOT_HULL, pOwner, COLLISION_GROUP_NONE, &m_traceHit );
if ( m_traceHit.fraction < 1.0 )
{
m_nHitActivity = ChooseIntersectionPointAndActivity( m_traceHit, g_bludgeonMins, g_bludgeonMaxs, pOwner );
}
}
// -------------------------
// Miss
// -------------------------
if ( m_traceHit.fraction == 1.0f )
{
m_nHitActivity = ACT_VM_MISSCENTER;
//Play swing sound
WeaponSound( SINGLE );
//Setup our next attack times
m_flNextPrimaryAttack = gpGlobals->curtime + CROWBAR_REFIRE_MISS;
}
else
{
Hit();
//Setup our next attack times
m_flNextPrimaryAttack = gpGlobals->curtime + CROWBAR_REFIRE_HIT;
}
//Send the anim
SendWeaponAnim( m_nHitActivity );
pOwner->SetAnimation( PLAYER_ATTACK1 );
}