mirror of
https://github.com/celisej567/source-engine.git
synced 2026-01-04 18:09:53 +03:00
1
This commit is contained in:
453
game/server/hl2/grenade_frag.cpp
Normal file
453
game/server/hl2/grenade_frag.cpp
Normal file
@@ -0,0 +1,453 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "basegrenade_shared.h"
|
||||
#include "grenade_frag.h"
|
||||
#include "Sprite.h"
|
||||
#include "SpriteTrail.h"
|
||||
#include "soundent.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
#define FRAG_GRENADE_BLIP_FREQUENCY 1.0f
|
||||
#define FRAG_GRENADE_BLIP_FAST_FREQUENCY 0.3f
|
||||
|
||||
#define FRAG_GRENADE_GRACE_TIME_AFTER_PICKUP 1.5f
|
||||
#define FRAG_GRENADE_WARN_TIME 1.5f
|
||||
|
||||
const float GRENADE_COEFFICIENT_OF_RESTITUTION = 0.2f;
|
||||
|
||||
ConVar sk_plr_dmg_fraggrenade ( "sk_plr_dmg_fraggrenade","0");
|
||||
ConVar sk_npc_dmg_fraggrenade ( "sk_npc_dmg_fraggrenade","0");
|
||||
ConVar sk_fraggrenade_radius ( "sk_fraggrenade_radius", "0");
|
||||
|
||||
#define GRENADE_MODEL "models/Weapons/w_grenade.mdl"
|
||||
|
||||
class CGrenadeFrag : public CBaseGrenade
|
||||
{
|
||||
DECLARE_CLASS( CGrenadeFrag, CBaseGrenade );
|
||||
|
||||
#if !defined( CLIENT_DLL )
|
||||
DECLARE_DATADESC();
|
||||
#endif
|
||||
|
||||
~CGrenadeFrag( void );
|
||||
|
||||
public:
|
||||
void Spawn( void );
|
||||
void OnRestore( void );
|
||||
void Precache( void );
|
||||
bool CreateVPhysics( void );
|
||||
void CreateEffects( void );
|
||||
void SetTimer( float detonateDelay, float warnDelay );
|
||||
void SetVelocity( const Vector &velocity, const AngularImpulse &angVelocity );
|
||||
int OnTakeDamage( const CTakeDamageInfo &inputInfo );
|
||||
void BlipSound() { EmitSound( "Grenade.Blip" ); }
|
||||
void DelayThink();
|
||||
void VPhysicsUpdate( IPhysicsObject *pPhysics );
|
||||
void OnPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t reason );
|
||||
void SetCombineSpawned( bool combineSpawned ) { m_combineSpawned = combineSpawned; }
|
||||
bool IsCombineSpawned( void ) const { return m_combineSpawned; }
|
||||
void SetPunted( bool punt ) { m_punted = punt; }
|
||||
bool WasPunted( void ) const { return m_punted; }
|
||||
|
||||
// this function only used in episodic.
|
||||
#if defined(HL2_EPISODIC) && 0 // FIXME: HandleInteraction() is no longer called now that base grenade derives from CBaseAnimating
|
||||
bool HandleInteraction(int interactionType, void *data, CBaseCombatCharacter* sourceEnt);
|
||||
#endif
|
||||
|
||||
void InputSetTimer( inputdata_t &inputdata );
|
||||
|
||||
protected:
|
||||
CHandle<CSprite> m_pMainGlow;
|
||||
CHandle<CSpriteTrail> m_pGlowTrail;
|
||||
|
||||
float m_flNextBlipTime;
|
||||
bool m_inSolid;
|
||||
bool m_combineSpawned;
|
||||
bool m_punted;
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( npc_grenade_frag, CGrenadeFrag );
|
||||
|
||||
BEGIN_DATADESC( CGrenadeFrag )
|
||||
|
||||
// Fields
|
||||
DEFINE_FIELD( m_pMainGlow, FIELD_EHANDLE ),
|
||||
DEFINE_FIELD( m_pGlowTrail, FIELD_EHANDLE ),
|
||||
DEFINE_FIELD( m_flNextBlipTime, FIELD_TIME ),
|
||||
DEFINE_FIELD( m_inSolid, FIELD_BOOLEAN ),
|
||||
DEFINE_FIELD( m_combineSpawned, FIELD_BOOLEAN ),
|
||||
DEFINE_FIELD( m_punted, FIELD_BOOLEAN ),
|
||||
|
||||
// Function Pointers
|
||||
DEFINE_THINKFUNC( DelayThink ),
|
||||
|
||||
// Inputs
|
||||
DEFINE_INPUTFUNC( FIELD_FLOAT, "SetTimer", InputSetTimer ),
|
||||
|
||||
END_DATADESC()
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
CGrenadeFrag::~CGrenadeFrag( void )
|
||||
{
|
||||
}
|
||||
|
||||
void CGrenadeFrag::Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
|
||||
SetModel( GRENADE_MODEL );
|
||||
|
||||
if( GetOwnerEntity() && GetOwnerEntity()->IsPlayer() )
|
||||
{
|
||||
m_flDamage = sk_plr_dmg_fraggrenade.GetFloat();
|
||||
m_DmgRadius = sk_fraggrenade_radius.GetFloat();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_flDamage = sk_npc_dmg_fraggrenade.GetFloat();
|
||||
m_DmgRadius = sk_fraggrenade_radius.GetFloat();
|
||||
}
|
||||
|
||||
m_takedamage = DAMAGE_YES;
|
||||
m_iHealth = 1;
|
||||
|
||||
SetSize( -Vector(4,4,4), Vector(4,4,4) );
|
||||
SetCollisionGroup( COLLISION_GROUP_WEAPON );
|
||||
CreateVPhysics();
|
||||
|
||||
BlipSound();
|
||||
m_flNextBlipTime = gpGlobals->curtime + FRAG_GRENADE_BLIP_FREQUENCY;
|
||||
|
||||
AddSolidFlags( FSOLID_NOT_STANDABLE );
|
||||
|
||||
m_combineSpawned = false;
|
||||
m_punted = false;
|
||||
|
||||
BaseClass::Spawn();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CGrenadeFrag::OnRestore( void )
|
||||
{
|
||||
// If we were primed and ready to detonate, put FX on us.
|
||||
if (m_flDetonateTime > 0)
|
||||
CreateEffects();
|
||||
|
||||
BaseClass::OnRestore();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CGrenadeFrag::CreateEffects( void )
|
||||
{
|
||||
// Start up the eye glow
|
||||
m_pMainGlow = CSprite::SpriteCreate( "sprites/redglow1.vmt", GetLocalOrigin(), false );
|
||||
|
||||
int nAttachment = LookupAttachment( "fuse" );
|
||||
|
||||
if ( m_pMainGlow != NULL )
|
||||
{
|
||||
m_pMainGlow->FollowEntity( this );
|
||||
m_pMainGlow->SetAttachment( this, nAttachment );
|
||||
m_pMainGlow->SetTransparency( kRenderGlow, 255, 255, 255, 200, kRenderFxNoDissipation );
|
||||
m_pMainGlow->SetScale( 0.2f );
|
||||
m_pMainGlow->SetGlowProxySize( 4.0f );
|
||||
}
|
||||
|
||||
// Start up the eye trail
|
||||
m_pGlowTrail = CSpriteTrail::SpriteTrailCreate( "sprites/bluelaser1.vmt", GetLocalOrigin(), false );
|
||||
|
||||
if ( m_pGlowTrail != NULL )
|
||||
{
|
||||
m_pGlowTrail->FollowEntity( this );
|
||||
m_pGlowTrail->SetAttachment( this, nAttachment );
|
||||
m_pGlowTrail->SetTransparency( kRenderTransAdd, 255, 0, 0, 255, kRenderFxNone );
|
||||
m_pGlowTrail->SetStartWidth( 8.0f );
|
||||
m_pGlowTrail->SetEndWidth( 1.0f );
|
||||
m_pGlowTrail->SetLifeTime( 0.5f );
|
||||
}
|
||||
}
|
||||
|
||||
bool CGrenadeFrag::CreateVPhysics()
|
||||
{
|
||||
// Create the object in the physics system
|
||||
VPhysicsInitNormal( SOLID_BBOX, 0, false );
|
||||
return true;
|
||||
}
|
||||
|
||||
// this will hit only things that are in newCollisionGroup, but NOT in collisionGroupAlreadyChecked
|
||||
class CTraceFilterCollisionGroupDelta : public CTraceFilterEntitiesOnly
|
||||
{
|
||||
public:
|
||||
// It does have a base, but we'll never network anything below here..
|
||||
DECLARE_CLASS_NOBASE( CTraceFilterCollisionGroupDelta );
|
||||
|
||||
CTraceFilterCollisionGroupDelta( const IHandleEntity *passentity, int collisionGroupAlreadyChecked, int newCollisionGroup )
|
||||
: m_pPassEnt(passentity), m_collisionGroupAlreadyChecked( collisionGroupAlreadyChecked ), m_newCollisionGroup( newCollisionGroup )
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
|
||||
{
|
||||
if ( !PassServerEntityFilter( pHandleEntity, m_pPassEnt ) )
|
||||
return false;
|
||||
CBaseEntity *pEntity = EntityFromEntityHandle( pHandleEntity );
|
||||
|
||||
if ( pEntity )
|
||||
{
|
||||
if ( g_pGameRules->ShouldCollide( m_collisionGroupAlreadyChecked, pEntity->GetCollisionGroup() ) )
|
||||
return false;
|
||||
if ( g_pGameRules->ShouldCollide( m_newCollisionGroup, pEntity->GetCollisionGroup() ) )
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected:
|
||||
const IHandleEntity *m_pPassEnt;
|
||||
int m_collisionGroupAlreadyChecked;
|
||||
int m_newCollisionGroup;
|
||||
};
|
||||
|
||||
void CGrenadeFrag::VPhysicsUpdate( IPhysicsObject *pPhysics )
|
||||
{
|
||||
BaseClass::VPhysicsUpdate( pPhysics );
|
||||
Vector vel;
|
||||
AngularImpulse angVel;
|
||||
pPhysics->GetVelocity( &vel, &angVel );
|
||||
|
||||
Vector start = GetAbsOrigin();
|
||||
// find all entities that my collision group wouldn't hit, but COLLISION_GROUP_NONE would and bounce off of them as a ray cast
|
||||
CTraceFilterCollisionGroupDelta filter( this, GetCollisionGroup(), COLLISION_GROUP_NONE );
|
||||
trace_t tr;
|
||||
|
||||
// UNDONE: Hull won't work with hitboxes - hits outer hull. But the whole point of this test is to hit hitboxes.
|
||||
#if 0
|
||||
UTIL_TraceHull( start, start + vel * gpGlobals->frametime, CollisionProp()->OBBMins(), CollisionProp()->OBBMaxs(), CONTENTS_HITBOX|CONTENTS_MONSTER|CONTENTS_SOLID, &filter, &tr );
|
||||
#else
|
||||
UTIL_TraceLine( start, start + vel * gpGlobals->frametime, CONTENTS_HITBOX|CONTENTS_MONSTER|CONTENTS_SOLID, &filter, &tr );
|
||||
#endif
|
||||
if ( tr.startsolid )
|
||||
{
|
||||
if ( !m_inSolid )
|
||||
{
|
||||
// UNDONE: Do a better contact solution that uses relative velocity?
|
||||
vel *= -GRENADE_COEFFICIENT_OF_RESTITUTION; // bounce backwards
|
||||
pPhysics->SetVelocity( &vel, NULL );
|
||||
}
|
||||
m_inSolid = true;
|
||||
return;
|
||||
}
|
||||
m_inSolid = false;
|
||||
if ( tr.DidHit() )
|
||||
{
|
||||
Vector dir = vel;
|
||||
VectorNormalize(dir);
|
||||
// send a tiny amount of damage so the character will react to getting bonked
|
||||
CTakeDamageInfo info( this, GetThrower(), pPhysics->GetMass() * vel, GetAbsOrigin(), 0.1f, DMG_CRUSH );
|
||||
tr.m_pEnt->TakeDamage( info );
|
||||
|
||||
// reflect velocity around normal
|
||||
vel = -2.0f * tr.plane.normal * DotProduct(vel,tr.plane.normal) + vel;
|
||||
|
||||
// absorb 80% in impact
|
||||
vel *= GRENADE_COEFFICIENT_OF_RESTITUTION;
|
||||
angVel *= -0.5f;
|
||||
pPhysics->SetVelocity( &vel, &angVel );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CGrenadeFrag::Precache( void )
|
||||
{
|
||||
PrecacheModel( GRENADE_MODEL );
|
||||
|
||||
PrecacheScriptSound( "Grenade.Blip" );
|
||||
|
||||
PrecacheModel( "sprites/redglow1.vmt" );
|
||||
PrecacheModel( "sprites/bluelaser1.vmt" );
|
||||
|
||||
BaseClass::Precache();
|
||||
}
|
||||
|
||||
void CGrenadeFrag::SetTimer( float detonateDelay, float warnDelay )
|
||||
{
|
||||
m_flDetonateTime = gpGlobals->curtime + detonateDelay;
|
||||
m_flWarnAITime = gpGlobals->curtime + warnDelay;
|
||||
SetThink( &CGrenadeFrag::DelayThink );
|
||||
SetNextThink( gpGlobals->curtime );
|
||||
|
||||
CreateEffects();
|
||||
}
|
||||
|
||||
void CGrenadeFrag::OnPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t reason )
|
||||
{
|
||||
SetThrower( pPhysGunUser );
|
||||
|
||||
#ifdef HL2MP
|
||||
SetTimer( FRAG_GRENADE_GRACE_TIME_AFTER_PICKUP, FRAG_GRENADE_GRACE_TIME_AFTER_PICKUP / 2);
|
||||
|
||||
BlipSound();
|
||||
m_flNextBlipTime = gpGlobals->curtime + FRAG_GRENADE_BLIP_FAST_FREQUENCY;
|
||||
m_bHasWarnedAI = true;
|
||||
#else
|
||||
if( IsX360() )
|
||||
{
|
||||
// Give 'em a couple of seconds to aim and throw.
|
||||
SetTimer( 2.0f, 1.0f);
|
||||
BlipSound();
|
||||
m_flNextBlipTime = gpGlobals->curtime + FRAG_GRENADE_BLIP_FAST_FREQUENCY;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HL2_EPISODIC
|
||||
SetPunted( true );
|
||||
#endif
|
||||
|
||||
BaseClass::OnPhysGunPickup( pPhysGunUser, reason );
|
||||
}
|
||||
|
||||
void CGrenadeFrag::DelayThink()
|
||||
{
|
||||
if( gpGlobals->curtime > m_flDetonateTime )
|
||||
{
|
||||
Detonate();
|
||||
return;
|
||||
}
|
||||
|
||||
if( !m_bHasWarnedAI && gpGlobals->curtime >= m_flWarnAITime )
|
||||
{
|
||||
#if !defined( CLIENT_DLL )
|
||||
CSoundEnt::InsertSound ( SOUND_DANGER, GetAbsOrigin(), 400, 1.5, this );
|
||||
#endif
|
||||
m_bHasWarnedAI = true;
|
||||
}
|
||||
|
||||
if( gpGlobals->curtime > m_flNextBlipTime )
|
||||
{
|
||||
BlipSound();
|
||||
|
||||
if( m_bHasWarnedAI )
|
||||
{
|
||||
m_flNextBlipTime = gpGlobals->curtime + FRAG_GRENADE_BLIP_FAST_FREQUENCY;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_flNextBlipTime = gpGlobals->curtime + FRAG_GRENADE_BLIP_FREQUENCY;
|
||||
}
|
||||
}
|
||||
|
||||
SetNextThink( gpGlobals->curtime + 0.1 );
|
||||
}
|
||||
|
||||
void CGrenadeFrag::SetVelocity( const Vector &velocity, const AngularImpulse &angVelocity )
|
||||
{
|
||||
IPhysicsObject *pPhysicsObject = VPhysicsGetObject();
|
||||
if ( pPhysicsObject )
|
||||
{
|
||||
pPhysicsObject->AddVelocity( &velocity, &angVelocity );
|
||||
}
|
||||
}
|
||||
|
||||
int CGrenadeFrag::OnTakeDamage( const CTakeDamageInfo &inputInfo )
|
||||
{
|
||||
// Manually apply vphysics because BaseCombatCharacter takedamage doesn't call back to CBaseEntity OnTakeDamage
|
||||
VPhysicsTakeDamage( inputInfo );
|
||||
|
||||
// Grenades only suffer blast damage and burn damage.
|
||||
if( !(inputInfo.GetDamageType() & (DMG_BLAST|DMG_BURN) ) )
|
||||
return 0;
|
||||
|
||||
return BaseClass::OnTakeDamage( inputInfo );
|
||||
}
|
||||
|
||||
#if defined(HL2_EPISODIC) && 0 // FIXME: HandleInteraction() is no longer called now that base grenade derives from CBaseAnimating
|
||||
extern int g_interactionBarnacleVictimGrab; ///< usually declared in ai_interactions.h but no reason to haul all of that in here.
|
||||
extern int g_interactionBarnacleVictimBite;
|
||||
extern int g_interactionBarnacleVictimReleased;
|
||||
bool CGrenadeFrag::HandleInteraction(int interactionType, void *data, CBaseCombatCharacter* sourceEnt)
|
||||
{
|
||||
// allow fragnades to be grabbed by barnacles.
|
||||
if ( interactionType == g_interactionBarnacleVictimGrab )
|
||||
{
|
||||
// give the grenade another five seconds seconds so the player can have the satisfaction of blowing up the barnacle with it
|
||||
float timer = m_flDetonateTime - gpGlobals->curtime + 5.0f;
|
||||
SetTimer( timer, timer - FRAG_GRENADE_WARN_TIME );
|
||||
|
||||
return true;
|
||||
}
|
||||
else if ( interactionType == g_interactionBarnacleVictimBite )
|
||||
{
|
||||
// detonate the grenade immediately
|
||||
SetTimer( 0, 0 );
|
||||
return true;
|
||||
}
|
||||
else if ( interactionType == g_interactionBarnacleVictimReleased )
|
||||
{
|
||||
// take the five seconds back off the timer.
|
||||
float timer = MAX(m_flDetonateTime - gpGlobals->curtime - 5.0f,0.0f);
|
||||
SetTimer( timer, timer - FRAG_GRENADE_WARN_TIME );
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return BaseClass::HandleInteraction( interactionType, data, sourceEnt );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void CGrenadeFrag::InputSetTimer( inputdata_t &inputdata )
|
||||
{
|
||||
SetTimer( inputdata.value.Float(), inputdata.value.Float() - FRAG_GRENADE_WARN_TIME );
|
||||
}
|
||||
|
||||
CBaseGrenade *Fraggrenade_Create( const Vector &position, const QAngle &angles, const Vector &velocity, const AngularImpulse &angVelocity, CBaseEntity *pOwner, float timer, bool combineSpawned )
|
||||
{
|
||||
// Don't set the owner here, or the player can't interact with grenades he's thrown
|
||||
CGrenadeFrag *pGrenade = (CGrenadeFrag *)CBaseEntity::Create( "npc_grenade_frag", position, angles, pOwner );
|
||||
|
||||
pGrenade->SetTimer( timer, timer - FRAG_GRENADE_WARN_TIME );
|
||||
pGrenade->SetVelocity( velocity, angVelocity );
|
||||
pGrenade->SetThrower( ToBaseCombatCharacter( pOwner ) );
|
||||
pGrenade->m_takedamage = DAMAGE_EVENTS_ONLY;
|
||||
pGrenade->SetCombineSpawned( combineSpawned );
|
||||
|
||||
return pGrenade;
|
||||
}
|
||||
|
||||
bool Fraggrenade_WasPunted( const CBaseEntity *pEntity )
|
||||
{
|
||||
const CGrenadeFrag *pFrag = dynamic_cast<const CGrenadeFrag *>( pEntity );
|
||||
if ( pFrag )
|
||||
{
|
||||
return pFrag->WasPunted();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Fraggrenade_WasCreatedByCombine( const CBaseEntity *pEntity )
|
||||
{
|
||||
const CGrenadeFrag *pFrag = dynamic_cast<const CGrenadeFrag *>( pEntity );
|
||||
if ( pFrag )
|
||||
{
|
||||
return pFrag->IsCombineSpawned();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
Reference in New Issue
Block a user