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.
657 lines
19 KiB
657 lines
19 KiB
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose:
|
|
//
|
|
//=============================================================================//
|
|
|
|
#include "cbase.h"
|
|
#include "basecsgrenade_projectile.h"
|
|
|
|
extern ConVar sv_gravity;
|
|
|
|
#ifdef CLIENT_DLL
|
|
|
|
#include "c_cs_player.h"
|
|
#include "hltvcamera.h"
|
|
#include "in_buttons.h"
|
|
#include <vgui/IInput.h>
|
|
#include "vgui_controls/Controls.h"
|
|
|
|
#else
|
|
|
|
#include "bot_manager.h"
|
|
#include "cs_player.h"
|
|
#include "soundent.h"
|
|
#include "te_effect_dispatch.h"
|
|
#include "keyvalues.h"
|
|
#include "cs_gamestats.h"
|
|
#include "cs_simple_hostage.h"
|
|
#include "Effects/chicken.h"
|
|
|
|
BEGIN_DATADESC( CBaseCSGrenadeProjectile )
|
|
DEFINE_THINKFUNC( DangerSoundThink ),
|
|
END_DATADESC()
|
|
|
|
#define GRENADE_FAILSAFE_MAX_BOUNCES 20
|
|
|
|
#endif
|
|
|
|
// NOTE: This has to be the last file included!
|
|
#include "tier0/memdbgon.h"
|
|
|
|
|
|
#ifdef CLIENT_DLL
|
|
extern ConVar spec_show_xray;
|
|
extern ConVar sv_grenade_trajectory;
|
|
extern ConVar sv_grenade_trajectory_time_spectator;
|
|
extern ConVar sv_grenade_trajectory_thickness;
|
|
extern ConVar sv_grenade_trajectory_dash;
|
|
#endif
|
|
|
|
|
|
IMPLEMENT_NETWORKCLASS_ALIASED( BaseCSGrenadeProjectile, DT_BaseCSGrenadeProjectile )
|
|
|
|
BEGIN_NETWORK_TABLE( CBaseCSGrenadeProjectile, DT_BaseCSGrenadeProjectile )
|
|
#ifdef CLIENT_DLL
|
|
RecvPropVector( RECVINFO( m_vInitialVelocity ) ),
|
|
RecvPropInt( RECVINFO( m_nBounces ) )
|
|
#else
|
|
SendPropVector( SENDINFO( m_vInitialVelocity ),
|
|
20, // nbits
|
|
0, // flags
|
|
-3000, // low value
|
|
3000 // high value
|
|
),
|
|
SendPropInt( SENDINFO( m_nBounces ) )
|
|
#endif
|
|
END_NETWORK_TABLE()
|
|
|
|
|
|
#ifdef CLIENT_DLL
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
CBaseCSGrenadeProjectile::~CBaseCSGrenadeProjectile()
|
|
{
|
|
flNextTrailLineTime = gpGlobals->curtime;
|
|
CreateGrenadeTrail();
|
|
}
|
|
|
|
void CBaseCSGrenadeProjectile::PostDataUpdate( DataUpdateType_t type )
|
|
{
|
|
BaseClass::PostDataUpdate( type );
|
|
|
|
//C_CSPlayer *pLocalPlayer = C_CSPlayer::GetLocalCSPlayer();
|
|
if ( type == DATA_UPDATE_CREATED )
|
|
{
|
|
SetNextClientThink( gpGlobals->curtime );
|
|
|
|
vecLastTrailLinePos = GetLocalOrigin();
|
|
flNextTrailLineTime = gpGlobals->curtime + 0.1;
|
|
|
|
// Now stick our initial velocity into the interpolation history
|
|
CInterpolatedVar< Vector > &interpolator = GetOriginInterpolator();
|
|
|
|
interpolator.ClearHistory();
|
|
float changeTime = GetLastChangeTime( LATCH_SIMULATION_VAR );
|
|
|
|
// Add a sample 1 second back.
|
|
Vector vCurOrigin = GetLocalOrigin() - m_vInitialVelocity;
|
|
interpolator.AddToHead( changeTime - 1.0, &vCurOrigin, false );
|
|
|
|
// Add the current sample.
|
|
vCurOrigin = GetLocalOrigin();
|
|
interpolator.AddToHead( changeTime, &vCurOrigin, false );
|
|
|
|
|
|
// IGameEvent * event = gameeventmanager->CreateEvent( "grenade_projectile_created" );
|
|
// C_CSPlayer *pPlayer = dynamic_cast<C_CSPlayer*>( GetThrower() );
|
|
// if( event && pPlayer )
|
|
// {
|
|
// event->SetInt( "owneruserid", pPlayer ? pPlayer->GetUserID() : 0 );
|
|
// event->SetInt( "grenade", GetGrenadeType() );
|
|
// gameeventmanager->FireEvent( event );
|
|
// }
|
|
|
|
//if ( pLocalPlayer )
|
|
// pLocalPlayer->NotifyPlayerOfThrownGrenade( this, GetThrower(), GetGrenadeType() );
|
|
}
|
|
else
|
|
{
|
|
|
|
}
|
|
}
|
|
|
|
void CBaseCSGrenadeProjectile::ClientThink( void )
|
|
{
|
|
BaseClass::ClientThink();
|
|
|
|
SetNextClientThink( gpGlobals->curtime );
|
|
|
|
if ( flNextTrailLineTime <= gpGlobals->curtime )
|
|
{
|
|
CreateGrenadeTrail();
|
|
}
|
|
}
|
|
|
|
void CBaseCSGrenadeProjectile::CreateGrenadeTrail( void )
|
|
{
|
|
C_CSPlayer *pLocalPlayer = C_CSPlayer::GetLocalCSPlayer();
|
|
if ( !pLocalPlayer )
|
|
return;
|
|
|
|
if ( flNextTrailLineTime <= gpGlobals->curtime )
|
|
{
|
|
bool bRenderForSpectator = CanSeeSpectatorOnlyTools() && spec_show_xray.GetInt();
|
|
if ( sv_grenade_trajectory_time_spectator.GetFloat() > 0.0f && sv_grenade_trajectory.GetInt() == 0 && pLocalPlayer && ( bRenderForSpectator || ( !pLocalPlayer->IsAlive() && ( pLocalPlayer->GetObserverMode() > OBS_MODE_FREEZECAM ) ) ) )
|
|
{
|
|
bool bRender = false;
|
|
|
|
if ( ( GetTeamNumber() == TEAM_CT ) && ( bRenderForSpectator || ( pLocalPlayer->GetTeamNumber() == TEAM_CT ) ) )
|
|
bRender = true;
|
|
else if ( ( GetTeamNumber() == TEAM_TERRORIST ) && ( bRenderForSpectator || ( pLocalPlayer->GetTeamNumber() == TEAM_TERRORIST ) ) )
|
|
bRender = true;
|
|
|
|
if ( bRender )
|
|
{
|
|
// Grenade trails for spectators
|
|
//CInterpolatedVar< Vector > &interpolator = GetOriginInterpolator();
|
|
|
|
QAngle angGrTrajAngles;
|
|
Vector vec3tempOrientation = ( vecLastTrailLinePos - GetLocalOrigin() );
|
|
VectorAngles( vec3tempOrientation, angGrTrajAngles );
|
|
|
|
float flGrTraThickness = sv_grenade_trajectory_thickness.GetFloat();
|
|
Vector vec3_GrTrajMin = Vector( 0, -flGrTraThickness, -flGrTraThickness );
|
|
Vector vec3_GrTrajMax = Vector( vec3tempOrientation.Length(), flGrTraThickness, flGrTraThickness );
|
|
bool bDotted = ( sv_grenade_trajectory_dash.GetInt() && ( fmod( gpGlobals->curtime, 0.1f ) < 0.05f ) );
|
|
|
|
Color traceColor;
|
|
if ( GetTeamNumber() == TEAM_CT )
|
|
{
|
|
traceColor[0] = 114;
|
|
traceColor[1] = 155;
|
|
traceColor[2] = 221;
|
|
}
|
|
else
|
|
{
|
|
traceColor[0] = 224;
|
|
traceColor[1] = 175;
|
|
traceColor[2] = 86;
|
|
}
|
|
|
|
if ( bDotted )
|
|
{
|
|
traceColor[0] /= 10;
|
|
traceColor[1] /= 10;
|
|
traceColor[2] /= 10;
|
|
}
|
|
|
|
//Add extruded box shapes to glow pass to build the arc
|
|
GlowObjectManager().AddGlowBox( GetLocalOrigin(), angGrTrajAngles, vec3_GrTrajMin, vec3_GrTrajMax, traceColor, sv_grenade_trajectory_time_spectator.GetFloat() );
|
|
|
|
//Make the grenade projectile itself glow
|
|
if ( !m_GlowObject.IsRendering() )
|
|
{
|
|
m_GlowObject.SetColor( Vector( traceColor[0] / 255.0f, traceColor[1] / 255.0f, traceColor[2] / 255.0f ) );
|
|
m_GlowObject.SetAlpha( 0.3f );
|
|
m_GlowObject.SetGlowAlphaCappedByRenderAlpha( true );
|
|
m_GlowObject.SetGlowAlphaFunctionOfMaxVelocity( 50.0f );
|
|
m_GlowObject.SetGlowAlphaMax( 0.3f );
|
|
m_GlowObject.SetRenderFlags( true, true );
|
|
}
|
|
}
|
|
}
|
|
|
|
vecLastTrailLinePos = GetLocalOrigin();
|
|
flNextTrailLineTime = gpGlobals->curtime + 0.05;
|
|
}
|
|
}
|
|
|
|
int CBaseCSGrenadeProjectile::DrawModel( int flags, const RenderableInstance_t &instance )
|
|
{
|
|
// During the first half-second of our life, don't draw ourselves if he's
|
|
// still playing his throw animation.
|
|
// (better yet, we could draw ourselves in his hand).
|
|
if ( GetThrower() != C_BasePlayer::GetLocalPlayer() )
|
|
{
|
|
if ( gpGlobals->curtime - m_flSpawnTime < 0.5 )
|
|
{
|
|
C_CSPlayer *pPlayer = dynamic_cast<C_CSPlayer*>( GetThrower() );
|
|
if ( pPlayer && pPlayer->m_PlayerAnimState->IsThrowingGrenade() )
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
return BaseClass::DrawModel( flags, instance );
|
|
}
|
|
|
|
void CBaseCSGrenadeProjectile::Spawn()
|
|
{
|
|
m_flSpawnTime = gpGlobals->curtime;
|
|
BaseClass::Spawn();
|
|
}
|
|
|
|
#else
|
|
|
|
void CBaseCSGrenadeProjectile::PostConstructor( const char *className )
|
|
{
|
|
BaseClass::PostConstructor( className );
|
|
TheBots->AddGrenade( this );
|
|
}
|
|
|
|
CBaseCSGrenadeProjectile::~CBaseCSGrenadeProjectile()
|
|
{
|
|
TheBots->RemoveGrenade( this );
|
|
}
|
|
|
|
void CBaseCSGrenadeProjectile::Precache()
|
|
{
|
|
BaseClass::Precache();
|
|
PrecacheEffect( "gunshotsplash" );
|
|
}
|
|
|
|
void CBaseCSGrenadeProjectile::Spawn( void )
|
|
{
|
|
Precache();
|
|
BaseClass::Spawn();
|
|
|
|
SetSolidFlags( FSOLID_NOT_STANDABLE );
|
|
SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_CUSTOM );
|
|
SetSolid( SOLID_BBOX ); // So it will collide with physics props!
|
|
AddFlag( FL_GRENADE );
|
|
|
|
m_lastHitPlayer = NULL;
|
|
|
|
// smaller, cube bounding box so we rest on the ground
|
|
Vector min = Vector( -GRENADE_DEFAULT_SIZE, -GRENADE_DEFAULT_SIZE, -GRENADE_DEFAULT_SIZE );
|
|
Vector max = Vector( GRENADE_DEFAULT_SIZE, GRENADE_DEFAULT_SIZE, GRENADE_DEFAULT_SIZE );
|
|
|
|
SetSize( min, max );
|
|
if ( CollisionProp( ) )
|
|
CollisionProp( )->SetCollisionBounds( min, max );
|
|
|
|
m_nBounces = 0;
|
|
}
|
|
|
|
int CBaseCSGrenadeProjectile::UpdateTransmitState()
|
|
{
|
|
// always call ShouldTransmit() for grenades
|
|
return SetTransmitState( FL_EDICT_FULLCHECK );
|
|
}
|
|
|
|
int CBaseCSGrenadeProjectile::ShouldTransmit( const CCheckTransmitInfo *pInfo )
|
|
{
|
|
CBaseEntity *pRecipientEntity = CBaseEntity::Instance( pInfo->m_pClientEnt );
|
|
if ( pRecipientEntity->IsPlayer() )
|
|
{
|
|
CBasePlayer *pRecipientPlayer = static_cast<CBasePlayer*>( pRecipientEntity );
|
|
|
|
// always transmit to the thrower of the grenade
|
|
if ( pRecipientPlayer && ( (GetThrower() && pRecipientPlayer == GetThrower()) ||
|
|
pRecipientPlayer->GetTeamNumber() == TEAM_SPECTATOR) )
|
|
{
|
|
return FL_EDICT_ALWAYS;
|
|
}
|
|
}
|
|
|
|
return FL_EDICT_PVSCHECK;
|
|
}
|
|
|
|
void CBaseCSGrenadeProjectile::DangerSoundThink( void )
|
|
{
|
|
if (!IsInWorld())
|
|
{
|
|
Remove( );
|
|
return;
|
|
}
|
|
|
|
if( gpGlobals->curtime > m_flDetonateTime )
|
|
{
|
|
Detonate();
|
|
return;
|
|
}
|
|
|
|
CSoundEnt::InsertSound ( SOUND_DANGER, GetAbsOrigin() + GetAbsVelocity() * 0.5, GetAbsVelocity().Length( ), 0.2 );
|
|
|
|
SetNextThink( gpGlobals->curtime + 0.2 );
|
|
|
|
if (GetWaterLevel() != WL_NotInWater)
|
|
{
|
|
SetAbsVelocity( GetAbsVelocity() * 0.5 );
|
|
}
|
|
}
|
|
|
|
|
|
//Sets the time at which the grenade will explode
|
|
void CBaseCSGrenadeProjectile::SetDetonateTimerLength( float timer )
|
|
{
|
|
m_flDetonateTime = gpGlobals->curtime + timer;
|
|
}
|
|
|
|
unsigned int CBaseCSGrenadeProjectile::PhysicsSolidMaskForEntity( void ) const
|
|
{
|
|
if ( GetCollisionGroup() == COLLISION_GROUP_DEBRIS )
|
|
{
|
|
return ((CONTENTS_GRENADECLIP | MASK_SOLID) & ~CONTENTS_MONSTER);
|
|
}
|
|
else
|
|
{
|
|
return (CONTENTS_GRENADECLIP|MASK_SOLID|MASK_VISIBLE_AND_NPCS|CONTENTS_HITBOX) & ~(CONTENTS_DEBRIS);
|
|
}
|
|
}
|
|
|
|
void CBaseCSGrenadeProjectile::ResolveFlyCollisionCustom( trace_t &trace, Vector &vecMove )
|
|
{
|
|
const float kSleepVelocity = 20.0f;
|
|
const float kSleepVelocitySquared = kSleepVelocity * kSleepVelocity;
|
|
|
|
// Verify that we have an entity.
|
|
CBaseEntity *pEntity = trace.m_pEnt;
|
|
Assert( pEntity );
|
|
|
|
if ( pEntity )
|
|
{
|
|
CChicken *pChicken = dynamic_cast< CChicken* >( pEntity );
|
|
if (pChicken)
|
|
{
|
|
// hurt the chicken
|
|
CTakeDamageInfo info( this, this, 10, DMG_CLUB );
|
|
pChicken->DispatchTraceAttack( info, GetAbsVelocity().Normalized(), &trace );
|
|
ApplyMultiDamage();
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
// if its breakable glass and we kill it, don't bounce.
|
|
// give some damage to the glass, and if it breaks, pass
|
|
// through it.
|
|
bool breakthrough = false;
|
|
|
|
if( pEntity && FClassnameIs( pEntity, "func_breakable" ) )
|
|
{
|
|
breakthrough = true;
|
|
}
|
|
|
|
if( pEntity && FClassnameIs( pEntity, "func_breakable_surf" ) )
|
|
{
|
|
breakthrough = true;
|
|
}
|
|
|
|
if( pEntity && FClassnameIs( pEntity, "prop_physics_multiplayer" ) && pEntity->GetMaxHealth() > 0 && pEntity->m_takedamage == DAMAGE_YES )
|
|
{
|
|
breakthrough = true;
|
|
}
|
|
|
|
// this one is tricky because BounceTouch hits breakable propers before we hit this function and the damage is already applied there (CBaseGrenade::BounceTouch( CBaseEntity *pOther ))
|
|
// by the time we hit this, the prop hasn't been removed yet, but it broke, is set to not take anymore damage and is marked for deletion - we have to cover this case here
|
|
if( pEntity && FClassnameIs( pEntity, "prop_dynamic" ) && pEntity->GetMaxHealth() > 0 && (pEntity->m_takedamage == DAMAGE_YES || (pEntity->m_takedamage == DAMAGE_NO && pEntity->IsEFlagSet( EFL_KILLME ))) )
|
|
{
|
|
breakthrough = true;
|
|
}
|
|
|
|
if ( breakthrough )
|
|
{
|
|
CTakeDamageInfo info( this, this, 10, DMG_CLUB );
|
|
pEntity->DispatchTraceAttack( info, GetAbsVelocity().Normalized(), &trace );
|
|
|
|
ApplyMultiDamage();
|
|
|
|
if( pEntity->m_iHealth <= 0 )
|
|
{
|
|
// slow our flight a little bit
|
|
Vector vel = GetAbsVelocity();
|
|
|
|
vel *= 0.4;
|
|
|
|
SetAbsVelocity( vel );
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
//Assume all surfaces have the same elasticity
|
|
float flSurfaceElasticity = 1.0;
|
|
|
|
//Don't bounce off of players with perfect elasticity
|
|
if ( pEntity && pEntity->IsPlayer() )
|
|
{
|
|
flSurfaceElasticity = 0.3f;
|
|
|
|
// and do slight damage to players on the opposite team
|
|
if ( GetTeamNumber() != pEntity->GetTeamNumber() )
|
|
{
|
|
CTakeDamageInfo info( this, GetThrower(), 2, DMG_GENERIC );
|
|
|
|
pEntity->TakeDamage( info );
|
|
}
|
|
}
|
|
|
|
//Don't bounce twice on a selection of problematic entities
|
|
bool bIsProjectile = dynamic_cast< CBaseCSGrenadeProjectile* >( pEntity ) != NULL;
|
|
if ( pEntity && !pEntity->IsWorld() && m_lastHitPlayer.Get() == pEntity )
|
|
{
|
|
bool bIsHostage = dynamic_cast< CHostage* >( pEntity ) != NULL;
|
|
if ( pEntity->IsPlayer() || bIsHostage || bIsProjectile )
|
|
{
|
|
//DevMsg( "Setting %s to DEBRIS, it is in group %i, it hit %s in group %i\n", this->GetClassname(), this->GetCollisionGroup(), pEntity->GetClassname(), pEntity->GetCollisionGroup() );
|
|
SetCollisionGroup( COLLISION_GROUP_DEBRIS );
|
|
if ( bIsProjectile )
|
|
{
|
|
//DevMsg( "Setting %s to DEBRIS, it is in group %i.\n", pEntity->GetClassname(), pEntity->GetCollisionGroup() );
|
|
pEntity->SetCollisionGroup( COLLISION_GROUP_DEBRIS );
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
if ( pEntity )
|
|
{
|
|
m_lastHitPlayer = pEntity;
|
|
}
|
|
|
|
float flTotalElasticity = GetElasticity() * flSurfaceElasticity;
|
|
flTotalElasticity = clamp( flTotalElasticity, 0.0f, 0.9f );
|
|
|
|
// NOTE: A backoff of 2.0f is a reflection
|
|
Vector vecAbsVelocity;
|
|
PhysicsClipVelocity( GetAbsVelocity(), trace.plane.normal, vecAbsVelocity, 2.0f );
|
|
vecAbsVelocity *= flTotalElasticity;
|
|
|
|
// Get the total velocity (player + conveyors, etc.)
|
|
VectorAdd( vecAbsVelocity, GetBaseVelocity(), vecMove );
|
|
float flSpeedSqr = DotProduct( vecMove, vecMove );
|
|
|
|
bool bIsWeapon = dynamic_cast< CBaseCombatWeapon* >( pEntity ) != NULL;
|
|
|
|
// Stop if on ground or if we bounce and our velocity is really low (keeps it from bouncing infinitely)
|
|
if ( pEntity &&
|
|
( ( trace.plane.normal.z > 0.7f ) || (trace.plane.normal.z > 0.1f && flSpeedSqr < kSleepVelocitySquared) ) &&
|
|
( pEntity->IsStandable() || bIsProjectile || bIsWeapon || pEntity->IsWorld() )
|
|
)
|
|
{
|
|
// clip it again to emulate old behavior and keep it from bouncing up like crazy when you throw it at the ground on the first toss
|
|
if ( flSpeedSqr > 96000 )
|
|
{
|
|
float alongDist = DotProduct( vecAbsVelocity.Normalized(), trace.plane.normal );
|
|
if ( alongDist > 0.5f )
|
|
{
|
|
float flBouncePadding = (1.0f - alongDist) + 0.5f;
|
|
vecAbsVelocity *= flBouncePadding;
|
|
}
|
|
}
|
|
|
|
SetAbsVelocity( vecAbsVelocity );
|
|
|
|
if ( flSpeedSqr < kSleepVelocitySquared )
|
|
{
|
|
SetGroundEntity( pEntity );
|
|
|
|
// Reset velocities.
|
|
SetAbsVelocity( vec3_origin );
|
|
SetLocalAngularVelocity( vec3_angle );
|
|
|
|
//align to the ground so we're not standing on end
|
|
QAngle angle;
|
|
VectorAngles( trace.plane.normal, angle );
|
|
|
|
// rotate randomly in yaw
|
|
angle[1] = random->RandomFloat( 0, 360 );
|
|
|
|
// TODO: rotate around trace.plane.normal
|
|
|
|
SetAbsAngles( angle );
|
|
}
|
|
else
|
|
{
|
|
Vector vecBaseDir = GetBaseVelocity();
|
|
if ( !vecBaseDir.IsZero() )
|
|
{
|
|
VectorNormalize( vecBaseDir );
|
|
Vector vecDelta = GetBaseVelocity() - vecAbsVelocity;
|
|
float flScale = vecDelta.Dot( vecBaseDir );
|
|
vecAbsVelocity += GetBaseVelocity() * flScale;
|
|
}
|
|
|
|
VectorScale( vecAbsVelocity, ( 1.0f - trace.fraction ) * gpGlobals->frametime, vecMove );
|
|
|
|
PhysicsPushEntity( vecMove, &trace );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SetAbsVelocity( vecAbsVelocity );
|
|
VectorScale( vecAbsVelocity, ( 1.0f - trace.fraction ) * gpGlobals->frametime, vecMove );
|
|
PhysicsPushEntity( vecMove, &trace );
|
|
}
|
|
|
|
BounceSound();
|
|
|
|
// tell the bots a grenade has bounced
|
|
CCSPlayer *player = ToCSPlayer(GetThrower());
|
|
if ( player )
|
|
{
|
|
IGameEvent * event = gameeventmanager->CreateEvent( "grenade_bounce" );
|
|
if ( event )
|
|
{
|
|
event->SetInt( "userid", player->GetUserID() );
|
|
event->SetFloat( "x", GetAbsOrigin().x );
|
|
event->SetFloat( "y", GetAbsOrigin().y );
|
|
event->SetFloat( "z", GetAbsOrigin().z );
|
|
gameeventmanager->FireEvent( event );
|
|
}
|
|
}
|
|
|
|
OnBounced();
|
|
|
|
if (m_nBounces > GRENADE_FAILSAFE_MAX_BOUNCES )
|
|
{
|
|
//failsafe detonate after 20 bounces
|
|
SetAbsVelocity( vec3_origin );
|
|
DetonateOnNextThink();
|
|
SetNextThink( gpGlobals->curtime );
|
|
SetMoveType( MOVETYPE_NONE );
|
|
}
|
|
else
|
|
{
|
|
m_nBounces++;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
void CBaseCSGrenadeProjectile::SetupInitialTransmittedGrenadeVelocity( const Vector &velocity )
|
|
{
|
|
m_vInitialVelocity = velocity;
|
|
}
|
|
|
|
#define MAX_WATER_SURFACE_DISTANCE 512
|
|
|
|
void CBaseCSGrenadeProjectile::Splash()
|
|
{
|
|
Vector centerPoint = GetAbsOrigin();
|
|
Vector normal( 0, 0, 1 );
|
|
|
|
// Find our water surface by tracing up till we're out of the water
|
|
trace_t tr;
|
|
Vector vecTrace( 0, 0, MAX_WATER_SURFACE_DISTANCE );
|
|
UTIL_TraceLine( centerPoint, centerPoint + vecTrace, MASK_WATER, NULL, COLLISION_GROUP_NONE, &tr );
|
|
|
|
// If we didn't start in water, we're above it
|
|
if ( tr.startsolid == false )
|
|
{
|
|
// Look downward to find the surface
|
|
vecTrace.Init( 0, 0, -MAX_WATER_SURFACE_DISTANCE );
|
|
UTIL_TraceLine( centerPoint, centerPoint + vecTrace, MASK_WATER, NULL, COLLISION_GROUP_NONE, &tr );
|
|
|
|
// If we hit it, setup the explosion
|
|
if ( tr.fraction < 1.0f )
|
|
{
|
|
centerPoint = tr.endpos;
|
|
}
|
|
else
|
|
{
|
|
//NOTENOTE: We somehow got into a splash without being near water?
|
|
Assert( 0 );
|
|
}
|
|
}
|
|
else if ( tr.fractionleftsolid )
|
|
{
|
|
// Otherwise we came out of the water at this point
|
|
centerPoint = centerPoint + (vecTrace * tr.fractionleftsolid);
|
|
}
|
|
else
|
|
{
|
|
// Use default values, we're really deep
|
|
}
|
|
|
|
CEffectData data;
|
|
data.m_vOrigin = centerPoint;
|
|
data.m_vNormal = normal;
|
|
data.m_flScale = random->RandomFloat( 1.0f, 2.0f );
|
|
|
|
if ( GetWaterType() & CONTENTS_SLIME )
|
|
{
|
|
data.m_fFlags |= FX_WATER_IN_SLIME;
|
|
}
|
|
|
|
DispatchEffect( "gunshotsplash", data );
|
|
}
|
|
|
|
// Add a row to ogs for the explosion of this grenade. Damage instances are recorded separately.
|
|
void CBaseCSGrenadeProjectile::RecordDetonation( void )
|
|
{
|
|
// I hate having to call this from so many places since half the grenades override the standard detonate and the rest dont...
|
|
// If this triggers, it's because either somebody changed the way some grenade code chains to base methods or it's taking an uncommon code path
|
|
// that needs to be investigated.
|
|
if ( m_bDetonationRecorded )
|
|
{
|
|
Warning( "Detonation of grenade '%s' attempted to record twice!\n", GetDebugName() );
|
|
Assert( 0 );
|
|
return;
|
|
}
|
|
|
|
CCSPlayer::StartNewBulletGroup();
|
|
|
|
SWeaponHitData *pHitData = new SWeaponHitData;
|
|
if ( pHitData->InitAsGrenadeDetonation( this, CCSPlayer::GetBulletGroup() ) )
|
|
{
|
|
CCS_GameStats.RecordWeaponHit( pHitData ); // submission deletes the struct.
|
|
m_bDetonationRecorded = true;
|
|
}
|
|
else
|
|
{
|
|
delete pHitData;
|
|
}
|
|
}
|
|
|
|
void CBaseCSGrenadeProjectile::Explode( trace_t *pTrace, int bitsDamageType )
|
|
{
|
|
RecordDetonation();
|
|
BaseClass::Explode( pTrace, bitsDamageType );
|
|
}
|
|
|
|
#endif // !CLIENT_DLL
|