diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/shared/tf/tf_weaponbase_grenadeproj.cpp | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'game/shared/tf/tf_weaponbase_grenadeproj.cpp')
| -rw-r--r-- | game/shared/tf/tf_weaponbase_grenadeproj.cpp | 861 |
1 files changed, 861 insertions, 0 deletions
diff --git a/game/shared/tf/tf_weaponbase_grenadeproj.cpp b/game/shared/tf/tf_weaponbase_grenadeproj.cpp new file mode 100644 index 0000000..e1ca443 --- /dev/null +++ b/game/shared/tf/tf_weaponbase_grenadeproj.cpp @@ -0,0 +1,861 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// +#include "cbase.h" +#include "tf_weaponbase_grenadeproj.h" +#include "tf_gamerules.h" + +// Client specific. +#ifdef CLIENT_DLL +#include "c_tf_player.h" +// Server specific. +#else +#include "soundent.h" +#include "te_effect_dispatch.h" +#include "tf_player.h" +#include "func_break.h" +#include "func_nogrenades.h" +#include "Sprite.h" +#include "tf_fx.h" +#include "halloween/merasmus/merasmus.h" +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +//============================================================================= +// +// TF Grenade projectile tables. +// + +// Server specific. +#ifdef GAME_DLL +BEGIN_DATADESC( CTFWeaponBaseGrenadeProj ) +DEFINE_THINKFUNC( DetonateThink ), +END_DATADESC() + +#ifdef STAGING_ONLY +ConVar tf_grenade_show_radius( "tf_grenade_show_radius", "0", FCVAR_CHEAT, "Render radius of grenades" ); +ConVar tf_grenade_show_radius_time( "tf_grenade_show_radius_time", "5.0", FCVAR_CHEAT, "Time to show grenade radius" ); +#endif // STAGING_ONLY + +extern void SendProxy_Origin( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID ); +extern void SendProxy_Angles( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID ); + +#endif + +IMPLEMENT_NETWORKCLASS_ALIASED( TFWeaponBaseGrenadeProj, DT_TFWeaponBaseGrenadeProj ) + +LINK_ENTITY_TO_CLASS( tf_weaponbase_grenade_proj, CTFWeaponBaseGrenadeProj ); +PRECACHE_REGISTER( tf_weaponbase_grenade_proj ); + +BEGIN_NETWORK_TABLE( CTFWeaponBaseGrenadeProj, DT_TFWeaponBaseGrenadeProj ) +#ifdef CLIENT_DLL + RecvPropVector( RECVINFO( m_vInitialVelocity ) ), + RecvPropBool( RECVINFO( m_bCritical ) ), + RecvPropInt( RECVINFO( m_iDeflected ) ), + + RecvPropVector( RECVINFO_NAME( m_vecNetworkOrigin, m_vecOrigin ) ), + RecvPropQAngles( RECVINFO_NAME( m_angNetworkAngles, m_angRotation ) ), + RecvPropEHandle( RECVINFO( m_hDeflectOwner )), + +#else + SendPropVector( SENDINFO( m_vInitialVelocity ), 20 /*nbits*/, 0 /*flags*/, -3000 /*low value*/, 3000 /*high value*/ ), + SendPropBool( SENDINFO( m_bCritical ) ), + + SendPropExclude( "DT_BaseEntity", "m_vecOrigin" ), + SendPropExclude( "DT_BaseEntity", "m_angRotation" ), + + SendPropVector (SENDINFO(m_vecOrigin), -1, SPROP_COORD_MP_INTEGRAL|SPROP_CHANGES_OFTEN, 0.0f, HIGH_DEFAULT, SendProxy_Origin ), + SendPropQAngles (SENDINFO(m_angRotation), 6, SPROP_CHANGES_OFTEN, SendProxy_Angles ), + SendPropInt( SENDINFO( m_iDeflected ), 4, SPROP_UNSIGNED ), + SendPropEHandle(SENDINFO( m_hDeflectOwner )), +#endif +END_NETWORK_TABLE() + +//----------------------------------------------------------------------------- +// Purpose: Constructor. +//----------------------------------------------------------------------------- +CTFWeaponBaseGrenadeProj::CTFWeaponBaseGrenadeProj() +{ +#ifndef CLIENT_DLL + m_bUseImpactNormal = false; + m_vecImpactNormal.Init(); + m_iDeflected = 0; + m_flDestroyableTime = 0.0f; + m_bIsMerasmusGrenade = false; + m_iDestroyableHitCount = 0; +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor. +//----------------------------------------------------------------------------- +CTFWeaponBaseGrenadeProj::~CTFWeaponBaseGrenadeProj() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int CTFWeaponBaseGrenadeProj::GetDamageType() +{ + int iDmgType = g_aWeaponDamageTypes[ GetWeaponID() ]; + if ( m_bCritical ) + { + iDmgType |= DMG_CRITICAL; + } + + return iDmgType; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int CTFWeaponBaseGrenadeProj::GetDamageCustom() +{ + return 0; +} + +const float GRENADE_COEFFICIENT_OF_RESTITUTION = 0.2f; + +//----------------------------------------------------------------------------- +// Purpose: Bounce backwards +//----------------------------------------------------------------------------- +void CTFWeaponBaseGrenadeProj::BounceOff( IPhysicsObject *pPhysics ) +{ + if ( !pPhysics ) + return; + + Vector vecVel; + pPhysics->GetVelocity( &vecVel, NULL ); + vecVel *= -GRENADE_COEFFICIENT_OF_RESTITUTION; + pPhysics->SetVelocity( &vecVel, NULL ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +float CTFWeaponBaseGrenadeProj::GetDamageRadius() +{ + float flRadius = m_DmgRadius; + CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( m_hLauncher, flRadius, mult_explosion_radius ); + return flRadius; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFWeaponBaseGrenadeProj::Precache( void ) +{ + BaseClass::Precache(); + +#ifndef CLIENT_DLL + PrecacheModel( NOGRENADE_SPRITE ); + PrecacheParticleSystem( "critical_grenade_blue" ); + PrecacheParticleSystem( "critical_grenade_red" ); +#endif +} + +//============================================================================= +// +// Client specific functions. +// +#ifdef CLIENT_DLL + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFWeaponBaseGrenadeProj::Spawn() +{ + m_flSpawnTime = gpGlobals->curtime; + BaseClass::Spawn(); + + AddFlag( FL_GRENADE ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFWeaponBaseGrenadeProj::OnDataChanged( DataUpdateType_t type ) +{ + BaseClass::OnDataChanged( type ); + + if ( type == DATA_UPDATE_CREATED ) + { + // 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 ); + } +} + +//============================================================================= +// +// Server specific functions. +// +#else + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CTFWeaponBaseGrenadeProj *CTFWeaponBaseGrenadeProj::Create( const char *szName, const Vector &position, const QAngle &angles, + const Vector &velocity, const AngularImpulse &angVelocity, + CBaseCombatCharacter *pOwner, const CTFWeaponInfo &weaponInfo, int iFlags ) +{ + CTFWeaponBaseGrenadeProj *pGrenade = static_cast<CTFWeaponBaseGrenadeProj*>( CBaseEntity::Create( szName, position, angles, pOwner ) ); + if ( pGrenade ) + { + pGrenade->InitGrenade( velocity, angVelocity, pOwner, weaponInfo ); + } + + return pGrenade; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFWeaponBaseGrenadeProj::InitGrenade( const Vector &velocity, const AngularImpulse &angVelocity, + CBaseCombatCharacter *pOwner, const CTFWeaponInfo &weaponInfo ) +{ + InitGrenade( velocity, angVelocity, pOwner, weaponInfo.GetWeaponData( TF_WEAPON_PRIMARY_MODE ).m_nDamage, weaponInfo.m_flDamageRadius ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFWeaponBaseGrenadeProj::InitGrenade( const Vector &velocity, const AngularImpulse &angVelocity, + CBaseCombatCharacter *pOwner, const int iDamage, const float flRadius ) +{ + // We can't use OwnerEntity for grenades, because then the owner can't shoot them with his hitscan weapons (due to collide rules) + // Thrower is used to store the person who threw the grenade, for damage purposes. + SetOwnerEntity( NULL ); + SetThrower( pOwner ); + + SetupInitialTransmittedGrenadeVelocity( velocity ); + + SetGravity( 0.4f/*BaseClass::GetGrenadeGravity()*/ ); + SetFriction( 0.2f/*BaseClass::GetGrenadeFriction()*/ ); + SetElasticity( 0.45f/*BaseClass::GetGrenadeElasticity()*/ ); + + SetDamage( iDamage ); + SetDamageRadius( flRadius ); + ChangeTeam( pOwner ? pOwner->GetTeamNumber() : TEAM_UNASSIGNED ); + + IPhysicsObject *pPhysicsObject = VPhysicsGetObject(); + if ( pPhysicsObject ) + { + pPhysicsObject->AddVelocity( &velocity, &angVelocity ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFWeaponBaseGrenadeProj::Spawn( void ) +{ + // Base class spawn. + BaseClass::Spawn(); + + // So it will collide with physics props! + SetSolidFlags( FSOLID_NOT_STANDABLE ); + SetSolid( SOLID_BBOX ); + + AddEffects( EF_NOSHADOW ); + + // Set the grenade size here. + UTIL_SetSize( this, Vector( -2.0f, -2.0f, -2.0f ), Vector( 2.0f, 2.0f, 2.0f ) ); + + // Set the movement type. + SetCollisionGroup( TF_COLLISIONGROUP_GRENADES ); + + VPhysicsInitNormal( SOLID_BBOX, 0, false ); + + m_takedamage = DAMAGE_EVENTS_ONLY; + + // Set the team. + ChangeTeam( GetThrower() ? GetThrower()->GetTeamNumber() : TEAM_UNASSIGNED ); + + // Set skin based on team ( red = 1, blue = 2 ) + m_nSkin = ( GetTeamNumber() == TF_TEAM_BLUE ) ? 1 : 0; + + m_flDestroyableTime = gpGlobals->curtime + TF_GRENADE_DESTROYABLE_TIMER; + + // Setup the think and touch functions (see CBaseEntity). + SetThink( &CTFWeaponBaseGrenadeProj::DetonateThink ); + SetNextThink( gpGlobals->curtime + 0.2 ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +#define TF_GRENADE_JUMP_RADIUS 146 +void CTFWeaponBaseGrenadeProj::Explode( trace_t *pTrace, int bitsDamageType ) +{ + if ( ShouldNotDetonate() ) + { + Destroy(); + return; + } + + SetModelName( NULL_STRING );//invisible + AddSolidFlags( FSOLID_NOT_SOLID ); + + m_takedamage = DAMAGE_NO; + + // Pull out of the wall a bit + if ( pTrace->fraction != 1.0 ) + { + SetAbsOrigin( pTrace->endpos + ( pTrace->plane.normal * 1.0f ) ); + } + + CSoundEnt::InsertSound ( SOUND_COMBAT, GetAbsOrigin(), BASEGRENADE_EXPLOSION_VOLUME, 3.0 ); + + // Explosion effect on client + Vector vecOrigin = GetAbsOrigin(); + CPVSFilter filter( vecOrigin ); + + + item_definition_index_t ownerWeaponDefIndex = INVALID_ITEM_DEF_INDEX; + CTFWeaponBase *pWeapon = dynamic_cast<CTFWeaponBase *>(GetOriginalLauncher()); + if (pWeapon) + { + ownerWeaponDefIndex = pWeapon->GetAttributeContainer()->GetItem()->GetItemDefIndex(); + } + + // Halloween Custom Spell Effect + int iHalloweenSpell = 0; + int iCustomParticleIndex = GetCustomParticleIndex(); + if ( TF_IsHolidayActive( kHoliday_HalloweenOrFullMoon ) ) + { + CALL_ATTRIB_HOOK_INT_ON_OTHER( m_hLauncher, iHalloweenSpell, halloween_pumpkin_explosions ); + if ( iHalloweenSpell > 0 ) + { + iCustomParticleIndex = GetParticleSystemIndex( "halloween_explosion" ); + } + } + + int iLargeExplosion = 0; + CALL_ATTRIB_HOOK_INT_ON_OTHER( m_hLauncher, iLargeExplosion, use_large_smoke_explosion ); + if ( iLargeExplosion > 0 ) + { + DispatchParticleEffect( "explosionTrail_seeds_mvm", GetAbsOrigin(), GetAbsAngles() ); + DispatchParticleEffect( "fluidSmokeExpl_ring_mvm", GetAbsOrigin(), GetAbsAngles() ); + } + + if ( UseImpactNormal() ) + { + if ( pTrace->m_pEnt && pTrace->m_pEnt->IsPlayer() ) + { + TE_TFExplosion(filter, 0.0f, vecOrigin, GetImpactNormal(), GetWeaponID(), pTrace->m_pEnt->entindex(), ownerWeaponDefIndex, SPECIAL1, iCustomParticleIndex); + } + else + { + TE_TFExplosion(filter, 0.0f, vecOrigin, GetImpactNormal(), GetWeaponID(), kInvalidEHandleExplosion, ownerWeaponDefIndex, SPECIAL1, iCustomParticleIndex); + } + } + else + { + if ( pTrace->m_pEnt && pTrace->m_pEnt->IsPlayer() ) + { + TE_TFExplosion(filter, 0.0f, vecOrigin, pTrace->plane.normal, GetWeaponID(), pTrace->m_pEnt->entindex(), ownerWeaponDefIndex, SPECIAL1, iCustomParticleIndex); + } + else + { + TE_TFExplosion(filter, 0.0f, vecOrigin, pTrace->plane.normal, GetWeaponID(), kInvalidEHandleExplosion, ownerWeaponDefIndex, SPECIAL1, iCustomParticleIndex); + } + } + + // Use the thrower's position as the reported position + Vector vecReported = GetThrower() ? GetThrower()->GetAbsOrigin() : vec3_origin; + int nCustomDamage = GetDamageCustom(); + CTakeDamageInfo info( this, GetThrower(), m_hLauncher, GetBlastForce(), GetAbsOrigin(), m_flDamage, bitsDamageType, nCustomDamage, &vecReported ); + + float flRadius = GetDamageRadius(); + +#ifdef STAGING_ONLY + if ( tf_grenade_show_radius.GetBool() ) + { + DrawRadius( flRadius ); + } +#endif + CTFRadiusDamageInfo radiusinfo( &info, vecOrigin, flRadius, NULL, TF_GRENADE_JUMP_RADIUS ); + TFGameRules()->RadiusDamage( radiusinfo ); + + // Don't decal players with scorch. + if ( pTrace->m_pEnt && !pTrace->m_pEnt->IsPlayer() ) + { + UTIL_DecalTrace( pTrace, "Scorch" ); + } + + if ( pTrace->m_pEnt && pTrace->m_pEnt->IsPlayer() && GetThrower() ) + { + CTFPlayer *pTarget = ToTFPlayer( GetEnemy() ); + if ( pTarget ) + { + if ( pTarget->GetTeamNumber() != GetThrower()->GetTeamNumber() ) + { + IGameEvent *event = gameeventmanager->CreateEvent( "projectile_direct_hit" ); + if ( event ) + { + event->SetInt( "attacker", GetThrower()->entindex() ); + event->SetInt( "victim", pTarget->entindex() ); + + gameeventmanager->FireEvent( event, true ); + } + } + } + } + + SetThink( &CBaseGrenade::SUB_Remove ); + SetTouch( NULL ); + + AddEffects( EF_NODRAW ); + SetAbsVelocity( vec3_origin ); + SetNextThink( gpGlobals->curtime ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int CTFWeaponBaseGrenadeProj::OnTakeDamage( const CTakeDamageInfo &info ) +{ + CTakeDamageInfo info2 = info; + + // Reduce explosion damage so that we don't get knocked too far + if ( info.GetDamageType() & DMG_BLAST ) + { + info2.ScaleDamageForce( 0.05 ); + } + + // We need to skip back to the base entity take damage, because + // CBaseCombatCharacter doesn't, which prevents us from reacting + // to physics impact damage. + return CBaseEntity::OnTakeDamage( info2 ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFWeaponBaseGrenadeProj::DetonateThink( void ) +{ + if ( !IsInWorld() ) + { + Remove( ); + return; + } + + if ( gpGlobals->curtime > m_flDetonateTime ) + { + Detonate(); + return; + } + + + SetNextThink( gpGlobals->curtime + 0.2 ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFWeaponBaseGrenadeProj::Detonate( void ) +{ + trace_t tr; + Vector vecSpot;// trace starts here! + + SetThink( NULL ); + + vecSpot = GetAbsOrigin() + Vector ( 0 , 0 , 8 ); + UTIL_TraceLine ( vecSpot, vecSpot + Vector ( 0, 0, -32 ), MASK_SHOT_HULL, this, COLLISION_GROUP_NONE, & tr); + + Explode( &tr, GetDamageType() ); + + if ( GetShakeAmplitude() ) + { + UTIL_ScreenShake( GetAbsOrigin(), GetShakeAmplitude(), 150.0, 1.0, GetShakeRadius(), SHAKE_START ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the time at which the grenade will explode. +//----------------------------------------------------------------------------- +void CTFWeaponBaseGrenadeProj::SetDetonateTimerLength( float timer ) +{ + float fFuseMult = 1.0f; + if ( GetOwnerEntity() ) + { + CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( GetOwnerEntity(), fFuseMult, fuse_mult ); + } + + m_flDetonateTime = gpGlobals->curtime + ( timer * fFuseMult ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFWeaponBaseGrenadeProj::ResolveFlyCollisionCustom( trace_t &trace, Vector &vecVelocity ) +{ + //Assume all surfaces have the same elasticity + float flSurfaceElasticity = 1.0; + + //Don't bounce off of players with perfect elasticity + if( trace.m_pEnt && trace.m_pEnt->IsPlayer() ) + { + flSurfaceElasticity = 0.3; + } + +#if 0 + // 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( trace.m_pEnt && FClassnameIs( trace.m_pEnt, "func_breakable" ) ) + { + breakthrough = true; + } + + if( trace.m_pEnt && FClassnameIs( trace.m_pEnt, "func_breakable_surf" ) ) + { + breakthrough = true; + } + + if (breakthrough) + { + CTakeDamageInfo info( this, this, 10, DMG_CLUB ); + trace.m_pEnt->DispatchTraceAttack( info, GetAbsVelocity(), &trace ); + + ApplyMultiDamage(); + + if( trace.m_pEnt->m_iHealth <= 0 ) + { + // slow our flight a little bit + Vector vel = GetAbsVelocity(); + + vel *= 0.4; + + SetAbsVelocity( vel ); + return; + } + } +#endif + + 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(), vecVelocity ); + float flSpeedSqr = DotProduct( vecVelocity, vecVelocity ); + + // Stop if on ground. + if ( trace.plane.normal.z > 0.7f ) // Floor + { + // Verify that we have an entity. + CBaseEntity *pEntity = trace.m_pEnt; + Assert( pEntity ); + + SetAbsVelocity( vecAbsVelocity ); + + if ( flSpeedSqr < ( 30 * 30 ) ) + { + if ( pEntity->IsStandable() ) + { + 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 ); + + // TFTODO: rotate around trace.plane.normal + + SetAbsAngles( angle ); + } + else + { + Vector vecDelta = GetBaseVelocity() - vecAbsVelocity; + Vector vecBaseDir = GetBaseVelocity(); + VectorNormalize( vecBaseDir ); + float flScale = vecDelta.Dot( vecBaseDir ); + + VectorScale( vecAbsVelocity, ( 1.0f - trace.fraction ) * gpGlobals->frametime, vecVelocity ); + VectorMA( vecVelocity, ( 1.0f - trace.fraction ) * gpGlobals->frametime, GetBaseVelocity() * flScale, vecVelocity ); + PhysicsPushEntity( vecVelocity, &trace ); + } + } + else + { + // If we get *too* slow, we'll stick without ever coming to rest because + // we'll get pushed down by gravity faster than we can escape from the wall. + if ( flSpeedSqr < ( 30 * 30 ) ) + { + // Reset velocities. + SetAbsVelocity( vec3_origin ); + SetLocalAngularVelocity( vec3_angle ); + } + else + { + SetAbsVelocity( vecAbsVelocity ); + } + } + + BounceSound(); + +#if 0 + // tell the bots a grenade has bounced + CCSPlayer *player = ToCSPlayer(GetThrower()); + if ( player ) + { + KeyValues *event = new KeyValues( "grenade_bounce" ); + event->SetInt( "userid", player->GetUserID() ); + gameeventmanager->FireEventServerOnly( event ); + } +#endif +} + +bool CTFWeaponBaseGrenadeProj::ShouldNotDetonate( void ) +{ + return InNoGrenadeZone( this ); +} + +void CTFWeaponBaseGrenadeProj::Destroy( bool bBlinkOut, bool bBreak ) +{ + if ( bBreak ) + { + CPVSFilter filter( GetAbsOrigin() ); + UserMessageBegin( filter, "BreakModelRocketDud" ); + WRITE_SHORT( GetModelIndex() ); + WRITE_VEC3COORD( GetAbsOrigin() ); + WRITE_ANGLES( GetAbsAngles() ); + MessageEnd(); + } + + // Kill it + SetThink( &BaseClass::SUB_Remove ); + SetNextThink( gpGlobals->curtime ); + SetTouch( NULL ); + AddEffects( EF_NODRAW ); + + if ( bBlinkOut ) + { + // Sprite flash + CSprite *pGlowSprite = CSprite::SpriteCreate( NOGRENADE_SPRITE, GetAbsOrigin(), false ); + if ( pGlowSprite ) + { + pGlowSprite->SetTransparency( kRenderGlow, 255, 255, 255, 255, kRenderFxFadeFast ); + pGlowSprite->SetThink( &CSprite::SUB_Remove ); + pGlowSprite->SetNextThink( gpGlobals->curtime + 1.0 ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: This will hit only things that are in newCollisionGroup, but NOT in collisionGroupAlreadyChecked +// Always ignores other grenade projectiles. +//----------------------------------------------------------------------------- +class CTraceFilterCollisionGrenades : public CTraceFilterEntitiesOnly +{ +public: + // It does have a base, but we'll never network anything below here.. + DECLARE_CLASS_NOBASE( CTraceFilterCollisionGrenades ); + + CTraceFilterCollisionGrenades( const IHandleEntity *passentity, const IHandleEntity *passentity2 ) + : m_pPassEnt(passentity), m_pPassEnt2(passentity2) + { + } + + virtual bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask ) + { + if ( !PassServerEntityFilter( pHandleEntity, m_pPassEnt ) ) + return false; + CBaseEntity *pEntity = EntityFromEntityHandle( pHandleEntity ); + if ( pEntity ) + { + if ( pEntity == m_pPassEnt2 ) + return false; + if ( pEntity->GetCollisionGroup() == TF_COLLISIONGROUP_GRENADES ) + return false; + if ( pEntity->GetCollisionGroup() == TFCOLLISION_GROUP_ROCKETS ) + return false; + if ( pEntity->GetCollisionGroup() == COLLISION_GROUP_DEBRIS ) + return false; + if ( pEntity->GetCollisionGroup() == TFCOLLISION_GROUP_RESPAWNROOMS ) + return false; + if ( pEntity->GetCollisionGroup() == COLLISION_GROUP_NONE ) + return false; + + return true; + } + + return true; + } + +protected: + const IHandleEntity *m_pPassEnt; + const IHandleEntity *m_pPassEnt2; +}; + + +//----------------------------------------------------------------------------- +// Purpose: Grenades aren't solid to players, so players don't get stuck on +// them when they're lying on the ground. We still want thrown grenades +// to bounce of players though, so manually trace ahead and see if we'd +// hit something that we'd like the grenade to "collide" with. +//----------------------------------------------------------------------------- +void CTFWeaponBaseGrenadeProj::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 + CTraceFilterCollisionGrenades filter( this, GetThrower() ); + + ITraceFilter *pFilterChain = NULL; + CTraceFilterIgnoreFriendlyCombatItems filterCombatItems( this, COLLISION_GROUP_NONE, GetTeamNumber(), true ); + if ( TFGameRules() && TFGameRules()->GameModeUsesUpgrades() ) + { + pFilterChain = &filterCombatItems; + } + CTraceFilterChain filterChain( &filter, pFilterChain ); + + trace_t tr; + UTIL_TraceLine( start, start + vel * gpGlobals->frametime, CONTENTS_HITBOX|CONTENTS_MONSTER|CONTENTS_SOLID, &filterChain, &tr ); + + bool bHitEnemy = tr.m_pEnt && tr.m_pEnt->GetTeamNumber() == GetEnemyTeam( GetTeamNumber() ); + bool bHitFriendly = tr.m_pEnt && tr.m_pEnt->GetTeamNumber() == GetTeamNumber() && CanCollideWithTeammates(); + + // Combat items are solid to enemy projectiles and bullets + if ( bHitEnemy && tr.m_pEnt->IsCombatItem() ) + { + if ( IsAllowedToExplode() ) + { + Explode( &tr, GetDamageType() ); + } + else + { + BounceOff( pPhysics ); + } + return; + } + + if ( tr.startsolid ) + { + if ( bHitEnemy ) + { + Touch( tr.m_pEnt ); + } + else if ( !m_bInSolid && bHitFriendly ) + { + BounceOff( pPhysics ); + } + m_bInSolid = true; + + return; + } + + m_bInSolid = false; + + if ( tr.DidHit() ) + { + Touch( tr.m_pEnt ); + + if ( bHitFriendly || bHitEnemy ) + { + // reflect velocity around normal + vel = -2.0f * tr.plane.normal * DotProduct(vel,tr.plane.normal) + vel; + + // absorb 80% in impact + vel *= GetElasticity(); + + if ( bHitEnemy == true ) + { + vel *= 0.5f; + } + + angVel *= -0.5f; + pPhysics->SetVelocity( &vel, &angVel ); + } + } +} + +#ifdef STAGING_ONLY +void CTFWeaponBaseGrenadeProj::DrawRadius( float flRadius ) +{ + Vector pos = GetAbsOrigin(); + int r = 255; + int g = 0, b = 0; + float flLifetime = tf_grenade_show_radius_time.GetFloat(); + bool bDepthTest = true; + + Vector edge, lastEdge; + NDebugOverlay::Line( pos, pos + Vector( 0, 0, 50 ), r, g, b, !bDepthTest, flLifetime ); + + lastEdge = Vector( flRadius + pos.x, pos.y, pos.z ); + float angle; + for( angle=0.0f; angle <= 360.0f; angle += 22.5f ) + { + edge.x = flRadius * cos( DEG2RAD( angle ) ) + pos.x; + edge.y = pos.y; + edge.z = flRadius * sin( DEG2RAD( angle ) ) + pos.z; + + NDebugOverlay::Line( edge, lastEdge, r, g, b, !bDepthTest, flLifetime ); + + lastEdge = edge; + } + + lastEdge = Vector( pos.x, flRadius + pos.y, pos.z ); + for( angle=0.0f; angle <= 360.0f; angle += 22.5f ) + { + edge.x = pos.x; + edge.y = flRadius * cos( DEG2RAD( angle ) ) + pos.y; + edge.z = flRadius * sin( DEG2RAD( angle ) ) + pos.z; + + NDebugOverlay::Line( edge, lastEdge, r, g, b, !bDepthTest, flLifetime ); + + lastEdge = edge; + } + + lastEdge = Vector( pos.x, flRadius + pos.y, pos.z ); + for( angle=0.0f; angle <= 360.0f; angle += 22.5f ) + { + edge.x = flRadius * cos( DEG2RAD( angle ) ) + pos.x; + edge.y = flRadius * sin( DEG2RAD( angle ) ) + pos.y; + edge.z = pos.z; + + NDebugOverlay::Line( edge, lastEdge, r, g, b, !bDepthTest, flLifetime ); + + lastEdge = edge; + } +} +#endif // STAGING_ONLY + +#endif |