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.
456 lines
11 KiB
456 lines
11 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: Hand grenade |
|
// |
|
// $NoKeywords: $ |
|
//=============================================================================// |
|
|
|
#include "cbase.h" |
|
#include "npcevent.h" |
|
#include "hl1mp_basecombatweapon_shared.h" |
|
//#include "basecombatcharacter.h" |
|
//#include "AI_BaseNPC.h" |
|
#ifdef CLIENT_DLL |
|
#include "hl1/hl1_c_player.h" |
|
#else |
|
#include "hl1_player.h" |
|
#endif |
|
#include "gamerules.h" |
|
#include "in_buttons.h" |
|
#ifdef CLIENT_DLL |
|
#else |
|
#include "soundent.h" |
|
#include "game.h" |
|
#endif |
|
#include "vstdlib/random.h" |
|
#include "engine/IEngineSound.h" |
|
#ifdef CLIENT_DLL |
|
#else |
|
#include "hl1_basegrenade.h" |
|
#endif |
|
|
|
|
|
#define HANDGRENADE_MODEL "models/w_grenade.mdl" |
|
|
|
|
|
#ifndef CLIENT_DLL |
|
|
|
extern ConVar sk_plr_dmg_grenade; |
|
|
|
//----------------------------------------------------------------------------- |
|
// CHandGrenade |
|
//----------------------------------------------------------------------------- |
|
LINK_ENTITY_TO_CLASS( grenade_hand, CHandGrenade ); |
|
|
|
BEGIN_DATADESC( CHandGrenade ) |
|
DEFINE_ENTITYFUNC( BounceTouch ), |
|
END_DATADESC() |
|
|
|
|
|
void CHandGrenade::Spawn( void ) |
|
{ |
|
SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE ); |
|
SetSolid( SOLID_BBOX ); |
|
AddSolidFlags( FSOLID_NOT_STANDABLE ); |
|
|
|
SetModel( HANDGRENADE_MODEL ); |
|
|
|
UTIL_SetSize( this, Vector( 0, 0, 0 ), Vector( 0, 0, 0 ) ); |
|
|
|
m_bHasWarnedAI = false; |
|
} |
|
|
|
|
|
void CHandGrenade::Precache( void ) |
|
{ |
|
BaseClass::Precache( ); |
|
|
|
PrecacheScriptSound( "Weapon_HandGrenade.GrenadeBounce" ); |
|
|
|
PrecacheModel( HANDGRENADE_MODEL ); |
|
} |
|
|
|
|
|
void CHandGrenade::ShootTimed( CBaseCombatCharacter *pOwner, Vector vecVelocity, float flTime ) |
|
{ |
|
SetAbsVelocity( vecVelocity ); |
|
|
|
SetThrower( pOwner ); |
|
SetOwnerEntity( pOwner ); |
|
|
|
SetTouch( &CHandGrenade::BounceTouch ); // Bounce if touched |
|
|
|
m_flDetonateTime = gpGlobals->curtime + flTime; |
|
SetThink( &CBaseGrenade::TumbleThink ); |
|
SetNextThink( gpGlobals->curtime + 0.1 ); |
|
if ( flTime < 0.1 ) |
|
{ |
|
SetNextThink( gpGlobals->curtime ); |
|
SetAbsVelocity( vec3_origin ); |
|
} |
|
|
|
// SetSequence( SelectWeightedSequence( ACT_GRENADE_TOSS ) ); |
|
SetSequence( 0 ); |
|
m_flPlaybackRate = 1.0; |
|
|
|
SetAbsAngles( QAngle( 0,0,60) ); |
|
|
|
AngularImpulse angImpulse; |
|
angImpulse[0] = random->RandomInt( -200, 200 ); |
|
angImpulse[1] = random->RandomInt( 400, 500 ); |
|
angImpulse[2] = random->RandomInt( -100, 100 ); |
|
ApplyLocalAngularVelocityImpulse( angImpulse ); |
|
|
|
SetGravity( UTIL_ScaleForGravity( 400 ) ); // use a lower gravity for grenades to make them easier to see |
|
SetFriction( 0.8 ); |
|
|
|
SetDamage( sk_plr_dmg_grenade.GetFloat() ); |
|
SetDamageRadius( GetDamage() * 2.5 ); |
|
} |
|
|
|
|
|
void CHandGrenade ::BounceSound( void ) |
|
{ |
|
EmitSound( "Weapon_HandGrenade.GrenadeBounce" ); |
|
} |
|
|
|
|
|
void CHandGrenade::BounceTouch( CBaseEntity *pOther ) |
|
{ |
|
if ( pOther->IsSolidFlagSet(FSOLID_TRIGGER | FSOLID_VOLUME_CONTENTS) ) |
|
return; |
|
|
|
// don't hit the guy that launched this grenade |
|
if ( pOther == GetThrower() ) |
|
return; |
|
|
|
// Do a special test for players |
|
if ( pOther->IsPlayer() ) |
|
{ |
|
// Never hit a player again (we'll explode and fixup anyway) |
|
SetCollisionGroup( COLLISION_GROUP_DEBRIS ); |
|
} |
|
// only do damage if we're moving fairly fast |
|
if ( (pOther->m_takedamage != DAMAGE_NO) && (m_flNextAttack < gpGlobals->curtime && GetAbsVelocity().Length() > 100)) |
|
{ |
|
if ( GetThrower() ) |
|
{ |
|
trace_t tr; |
|
tr = CBaseEntity::GetTouchTrace( ); |
|
ClearMultiDamage( ); |
|
Vector forward; |
|
AngleVectors( GetAbsAngles(), &forward ); |
|
|
|
CTakeDamageInfo info( this, GetThrower(), 1, DMG_CLUB ); |
|
CalculateMeleeDamageForce( &info, forward, tr.endpos ); |
|
pOther->DispatchTraceAttack( info, forward, &tr ); |
|
ApplyMultiDamage(); |
|
} |
|
m_flNextAttack = gpGlobals->curtime + 1.0; // debounce |
|
} |
|
|
|
Vector vecTestVelocity; |
|
// m_vecAngVelocity = Vector (300, 300, 300); |
|
|
|
// this is my heuristic for modulating the grenade velocity because grenades dropped purely vertical |
|
// or thrown very far tend to slow down too quickly for me to always catch just by testing velocity. |
|
// trimming the Z velocity a bit seems to help quite a bit. |
|
vecTestVelocity = GetAbsVelocity(); |
|
vecTestVelocity.z *= 0.45; |
|
|
|
if ( !m_bHasWarnedAI && vecTestVelocity.Length() <= 60 ) |
|
{ |
|
// grenade is moving really slow. It's probably very close to where it will ultimately stop moving. |
|
// emit the danger sound. |
|
|
|
// register a radius louder than the explosion, so we make sure everyone gets out of the way |
|
CSoundEnt::InsertSound ( SOUND_DANGER, GetAbsOrigin(), m_flDamage / 0.4, 0.3 ); |
|
m_bHasWarnedAI = TRUE; |
|
} |
|
|
|
// HACKHACK - On ground isn't always set, so look for ground underneath |
|
trace_t tr; |
|
UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() - Vector(0,0,10), MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr ); |
|
|
|
if ( tr.fraction < 1.0 ) |
|
{ |
|
// add a bit of static friction |
|
// SetAbsVelocity( GetAbsVelocity() * 0.8 ); |
|
SetSequence( SelectWeightedSequence( ACT_IDLE ) ); |
|
SetAbsAngles( vec3_angle ); |
|
} |
|
|
|
// play bounce sound |
|
BounceSound(); |
|
|
|
m_flPlaybackRate = GetAbsVelocity().Length() / 200.0; |
|
if (m_flPlaybackRate > 1.0) |
|
m_flPlaybackRate = 1; |
|
else if (m_flPlaybackRate < 0.5) |
|
m_flPlaybackRate = 0; |
|
} |
|
|
|
#endif |
|
|
|
#ifdef CLIENT_DLL |
|
#define CWeaponHandGrenade C_WeaponHandGrenade |
|
#endif |
|
|
|
//----------------------------------------------------------------------------- |
|
// CWeaponHandGrenade |
|
//----------------------------------------------------------------------------- |
|
|
|
class CWeaponHandGrenade : public CBaseHL1MPCombatWeapon |
|
{ |
|
DECLARE_CLASS( CWeaponHandGrenade, CBaseHL1MPCombatWeapon ); |
|
public: |
|
|
|
DECLARE_NETWORKCLASS(); |
|
DECLARE_PREDICTABLE(); |
|
|
|
CWeaponHandGrenade( void ); |
|
|
|
void Precache( void ); |
|
void PrimaryAttack( void ); |
|
void WeaponIdle( void ); |
|
bool Deploy( void ); |
|
bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL ); |
|
|
|
// DECLARE_SERVERCLASS(); |
|
DECLARE_DATADESC(); |
|
|
|
private: |
|
// float m_flStartThrow; |
|
// float m_flReleaseThrow; |
|
CNetworkVar( float, m_flStartThrow ); |
|
CNetworkVar( float, m_flReleaseThrow ); |
|
}; |
|
|
|
IMPLEMENT_NETWORKCLASS_ALIASED( WeaponHandGrenade, DT_WeaponHandGrenade ); |
|
|
|
BEGIN_NETWORK_TABLE( CWeaponHandGrenade, DT_WeaponHandGrenade ) |
|
#ifdef CLIENT_DLL |
|
RecvPropFloat( RECVINFO( m_flStartThrow ) ), |
|
RecvPropFloat( RECVINFO( m_flReleaseThrow ) ), |
|
#else |
|
SendPropFloat( SENDINFO( m_flStartThrow ) ), |
|
SendPropFloat( SENDINFO( m_flReleaseThrow ) ), |
|
#endif |
|
END_NETWORK_TABLE() |
|
|
|
BEGIN_PREDICTION_DATA( CWeaponHandGrenade ) |
|
#ifdef CLIENT_DLL |
|
DEFINE_PRED_FIELD( m_flStartThrow, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ), |
|
DEFINE_PRED_FIELD( m_flReleaseThrow, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ), |
|
#endif |
|
END_PREDICTION_DATA() |
|
|
|
LINK_ENTITY_TO_CLASS( weapon_handgrenade, CWeaponHandGrenade ); |
|
|
|
PRECACHE_WEAPON_REGISTER( weapon_handgrenade ); |
|
|
|
//IMPLEMENT_SERVERCLASS_ST( CWeaponHandGrenade, DT_WeaponHandGrenade ) |
|
//END_SEND_TABLE() |
|
|
|
BEGIN_DATADESC( CWeaponHandGrenade ) |
|
END_DATADESC() |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Constructor |
|
//----------------------------------------------------------------------------- |
|
CWeaponHandGrenade::CWeaponHandGrenade( void ) |
|
{ |
|
m_bReloadsSingly = false; |
|
m_bFiresUnderwater = true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CWeaponHandGrenade::Precache( void ) |
|
{ |
|
#ifndef CLIENT_DLL |
|
UTIL_PrecacheOther( "grenade_hand" ); |
|
#endif |
|
|
|
BaseClass::Precache(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CWeaponHandGrenade::PrimaryAttack( void ) |
|
{ |
|
CBasePlayer *pPlayer = ToBasePlayer( GetOwner() ); |
|
if ( !pPlayer ) |
|
{ |
|
return; |
|
} |
|
|
|
if ( ( m_flStartThrow <= 0 ) && pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) > 0 ) |
|
{ |
|
m_flStartThrow = gpGlobals->curtime; |
|
m_flReleaseThrow = 0; |
|
|
|
SendWeaponAnim( ACT_VM_PRIMARYATTACK ); |
|
SetWeaponIdleTime( gpGlobals->curtime + 0.5 ); |
|
} |
|
} |
|
|
|
|
|
void CWeaponHandGrenade::WeaponIdle( void ) |
|
{ |
|
if ( m_flReleaseThrow == 0 && m_flStartThrow ) |
|
m_flReleaseThrow = gpGlobals->curtime; |
|
|
|
if ( !HasWeaponIdleTimeElapsed() ) |
|
return; |
|
|
|
CBasePlayer *pPlayer = ToBasePlayer( GetOwner() ); |
|
if ( !pPlayer ) |
|
{ |
|
return; |
|
} |
|
|
|
if ( m_flStartThrow ) |
|
{ |
|
Vector vecAiming = pPlayer->GetAutoaimVector( 0 ); |
|
|
|
QAngle angThrow; |
|
VectorAngles( vecAiming, angThrow ); |
|
|
|
Vector vecUp; |
|
Vector vecRight; |
|
AngleVectors( angThrow, NULL, &vecRight, &vecUp ); |
|
|
|
if ( angThrow.x > 180 ) // player is pitching up |
|
angThrow.x = -15 - ( 360 - angThrow.x ) * ( ( 90 - 10 ) / 90.0 ); |
|
else // player is pitching down |
|
angThrow.x = -15 + angThrow.x * ( ( 90 + 10 ) / 90.0 ); |
|
|
|
float flVel = ( 90 - angThrow.x ) * 4; |
|
if ( flVel > 500 ) |
|
flVel = 500; |
|
|
|
Vector vecFwd; |
|
AngleVectors( angThrow, &vecFwd ); |
|
|
|
Vector vecSrc = pPlayer->EyePosition() + vecFwd * 16; |
|
Vector vecThrow = vecFwd * flVel + pPlayer->GetAbsVelocity(); |
|
|
|
QAngle angles; |
|
VectorAngles( vecThrow, angles ); |
|
#ifndef CLIENT_DLL |
|
CHandGrenade *pGrenade = (CHandGrenade*)Create( "grenade_hand", vecSrc, angles ); |
|
if ( pGrenade ) |
|
{ |
|
// always explode 3 seconds after the pin was pulled |
|
float flTime = m_flStartThrow - gpGlobals->curtime + 3.0; |
|
if ( flTime < 0 ) |
|
{ |
|
flTime = 0; |
|
} |
|
|
|
pGrenade->ShootTimed( pPlayer, vecThrow, flTime ); |
|
} |
|
#endif |
|
|
|
if ( flVel < 500 ) |
|
{ |
|
SendWeaponAnim( ACT_HANDGRENADE_THROW1 ); |
|
} |
|
else if ( flVel < 1000 ) |
|
{ |
|
SendWeaponAnim( ACT_HANDGRENADE_THROW2 ); |
|
} |
|
else |
|
{ |
|
SendWeaponAnim( ACT_HANDGRENADE_THROW3 ); |
|
} |
|
|
|
// player "shoot" animation |
|
pPlayer->SetAnimation( PLAYER_ATTACK1 ); |
|
|
|
m_flReleaseThrow = 0; |
|
m_flStartThrow = 0; |
|
|
|
SetWeaponIdleTime( gpGlobals->curtime + 0.5 ); |
|
m_flNextPrimaryAttack = gpGlobals->curtime + 0.5; |
|
m_flNextSecondaryAttack = gpGlobals->curtime + 0.5; |
|
|
|
pPlayer->RemoveAmmo( 1, m_iPrimaryAmmoType ); |
|
|
|
return; |
|
} |
|
else if ( m_flReleaseThrow > 0 ) |
|
{ |
|
// we've finished the throw, restart. |
|
m_flStartThrow = 0; |
|
|
|
if ( pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) > 0 ) |
|
{ |
|
SendWeaponAnim( ACT_VM_DRAW ); |
|
} |
|
else |
|
{ |
|
// RetireWeapon(); |
|
return; |
|
} |
|
|
|
SetWeaponIdleTime( gpGlobals->curtime + random->RandomFloat( 10, 15 ) ); |
|
m_flReleaseThrow = -1; |
|
return; |
|
} |
|
|
|
if ( pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) > 0 ) |
|
{ |
|
float flRand = random->RandomFloat( 0, 1 ); |
|
if ( flRand <= 0.75 ) |
|
{ |
|
SendWeaponAnim( ACT_VM_IDLE ); |
|
SetWeaponIdleTime( gpGlobals->curtime + random->RandomFloat( 10, 15 ) );// how long till we do this again. |
|
} |
|
else |
|
{ |
|
SendWeaponAnim( ACT_VM_FIDGET ); |
|
} |
|
} |
|
} |
|
|
|
bool CWeaponHandGrenade::Deploy( void ) |
|
{ |
|
m_flReleaseThrow = -1; |
|
|
|
return DefaultDeploy( (char*)GetViewModel(), (char*)GetWorldModel(), ACT_VM_DRAW, (char*)GetAnimPrefix() ); |
|
} |
|
|
|
bool CWeaponHandGrenade::Holster( CBaseCombatWeapon *pSwitchingTo ) |
|
{ |
|
CBasePlayer *pPlayer = ToBasePlayer( GetOwner() ); |
|
if ( !pPlayer ) |
|
{ |
|
return false; |
|
} |
|
|
|
if ( m_flStartThrow > 0 ) |
|
{ |
|
return false; |
|
} |
|
|
|
if ( !BaseClass::Holster( pSwitchingTo ) ) |
|
{ |
|
return false; |
|
} |
|
|
|
if ( pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) <= 0 ) |
|
{ |
|
#ifndef CLIENT_DLL |
|
SetThink( &CWeaponHandGrenade::DestroyItem ); |
|
SetNextThink( gpGlobals->curtime + 0.1 ); |
|
#endif |
|
} |
|
|
|
pPlayer->SetNextAttack( gpGlobals->curtime + 0.5 ); |
|
|
|
return true; |
|
}
|
|
|