summaryrefslogtreecommitdiff
path: root/game/shared/tf/tf_projectile_base.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'game/shared/tf/tf_projectile_base.cpp')
-rw-r--r--game/shared/tf/tf_projectile_base.cpp495
1 files changed, 495 insertions, 0 deletions
diff --git a/game/shared/tf/tf_projectile_base.cpp b/game/shared/tf/tf_projectile_base.cpp
new file mode 100644
index 0000000..350915e
--- /dev/null
+++ b/game/shared/tf/tf_projectile_base.cpp
@@ -0,0 +1,495 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: TF Base Rockets.
+//
+//=============================================================================//
+#include "cbase.h"
+#include "tf_projectile_base.h"
+#include "effect_dispatch_data.h"
+#include "tf_shareddefs.h"
+#include "tf_gamerules.h"
+
+#ifdef GAME_DLL
+#include "te_effect_dispatch.h"
+#else
+#include "c_te_effect_dispatch.h"
+#endif
+#ifdef CLIENT_DLL
+#include "c_basetempentity.h"
+#include "c_te_legacytempents.h"
+#include "c_te_effect_dispatch.h"
+#include "input.h"
+#include "c_tf_player.h"
+#else
+#include "tf_player.h"
+#endif
+
+#ifdef _DEBUG
+ConVar tf_debug_projectile( "tf_debug_projectile", "0", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT );
+#endif // _DEBUG
+
+IMPLEMENT_NETWORKCLASS_ALIASED( TFBaseProjectile, DT_TFBaseProjectile )
+
+BEGIN_NETWORK_TABLE( CTFBaseProjectile, DT_TFBaseProjectile )
+#ifdef CLIENT_DLL
+ RecvPropVector( RECVINFO( m_vInitialVelocity ) ),
+ RecvPropEHandle( RECVINFO( m_hLauncher ) )
+#else
+ SendPropVector( SENDINFO( m_vInitialVelocity ), 20 /*nbits*/, 0 /*flags*/, -3000 /*low value*/, 3000 /*high value*/ ),
+ SendPropEHandle( SENDINFO( m_hLauncher ) )
+#endif
+END_NETWORK_TABLE()
+
+// Server specific.
+#ifdef GAME_DLL
+
+BEGIN_DATADESC( CTFBaseProjectile )
+ //DEFINE_FUNCTION( ProjectileTouch ),
+ DEFINE_THINKFUNC( FlyThink ),
+END_DATADESC()
+
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor.
+//-----------------------------------------------------------------------------
+CTFBaseProjectile::CTFBaseProjectile()
+{
+ m_vInitialVelocity.Init();
+
+ SetWeaponID( TF_WEAPON_NONE );
+
+ // Client specific.
+#ifdef CLIENT_DLL
+
+ m_flSpawnTime = 0.0f;
+
+ // Server specific.
+#else
+
+ m_flDamage = 0.0f;
+
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor.
+//-----------------------------------------------------------------------------
+CTFBaseProjectile::~CTFBaseProjectile()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFBaseProjectile::Precache( void )
+{
+#ifdef GAME_DLL
+ PrecacheModel( GetProjectileModelName() );
+#endif
+ BaseClass::Precache();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFBaseProjectile::Spawn( void )
+{
+ // Client specific.
+#ifdef CLIENT_DLL
+
+ m_flSpawnTime = gpGlobals->curtime;
+
+ BaseClass::Spawn();
+
+ // Server specific.
+#else
+
+ // Precache.
+ Precache();
+
+ SetModel( GetProjectileModelName() );
+
+ SetSolid( SOLID_BBOX );
+ SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_CUSTOM );
+ AddEFlags( EFL_NO_WATER_VELOCITY_CHANGE );
+
+ UTIL_SetSize( this, -Vector( 1.0f, 1.0f, 1.0f ), Vector( 1.0f, 1.0f, 1.0f ) );
+
+ // Setup attributes.
+ SetGravity( GetGravity() );
+ m_takedamage = DAMAGE_NO;
+ SetDamage( 25.0f );
+
+ SetCollisionGroup( COLLISION_GROUP_PROJECTILE );
+
+ // Setup the touch and think functions.
+ SetTouch( &CTFBaseProjectile::ProjectileTouch );
+ SetThink( &CTFBaseProjectile::FlyThink );
+ SetNextThink( gpGlobals->curtime );
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CTFBaseProjectile *CTFBaseProjectile::Create( const char *pszClassname, const Vector &vecOrigin,
+ const QAngle &vecAngles, CBaseEntity *pOwner, float flVelocity, short iProjModelIndex, const char *pszDispatchEffect,
+ CBaseEntity *pScorer, bool bCritical, Vector vColor1, Vector vColor2 )
+{
+ CTFBaseProjectile *pProjectile = NULL;
+
+ Vector vecForward, vecRight, vecUp;
+ AngleVectors( vecAngles, &vecForward, &vecRight, &vecUp );
+
+ Vector vecVelocity = vecForward * flVelocity;
+
+#ifdef GAME_DLL
+ pProjectile = static_cast<CTFBaseProjectile*>( CBaseEntity::Create( pszClassname, vecOrigin, vecAngles, pOwner ) );
+ if ( !pProjectile )
+ return NULL;
+
+ // Initialize the owner.
+ pProjectile->SetOwnerEntity( pOwner );
+
+ pProjectile->SetScorer( pScorer );
+
+ // Spawn.
+ pProjectile->Spawn();
+
+ pProjectile->SetAbsVelocity( vecVelocity );
+ //pProjectile->SetupInitialTransmittedGrenadeVelocity( vecVelocity );
+
+ // Setup the initial angles.
+ QAngle angles;
+ VectorAngles( vecVelocity, angles );
+ pProjectile->SetAbsAngles( angles );
+
+ // Set team.
+ pProjectile->ChangeTeam( pOwner->GetTeamNumber() );
+
+ // Hide the projectile and create a fake one on the client
+ pProjectile->AddEffects( EF_NODRAW );
+#endif
+
+ if ( pszDispatchEffect )
+ {
+ // we'd like to just send this projectile to a person in the shooter's PAS. However
+ // the projectile won't be sent to a player outside of water if shot from inside water
+ // and vice-versa, so we do a trace here to figure out if the trace starts or stops in water.
+ // if it crosses contents, we'll just broadcast the projectile. Otherwise, just send to PVS
+ // of the trace's endpoint.
+ trace_t tr;
+ CTraceFilterSimple traceFilter( pOwner, COLLISION_GROUP_NONE );
+ ITraceFilter *pFilterChain = NULL;
+
+ CTraceFilterIgnoreFriendlyCombatItems traceFilterCombatItem( pOwner, COLLISION_GROUP_NONE, pOwner->GetTeamNumber() );
+ if ( TFGameRules() && TFGameRules()->GameModeUsesUpgrades() )
+ {
+ // Ignore teammates and their (physical) upgrade items in MvM
+ pFilterChain = &traceFilterCombatItem;
+ }
+
+ CTraceFilterChain traceFilterChain( &traceFilter, pFilterChain );
+ UTIL_TraceLine( vecOrigin, vecOrigin + vecForward * MAX_COORD_RANGE, (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_WINDOW|CONTENTS_GRATE), &traceFilterChain, &tr );
+
+ bool bBroadcast = ( UTIL_PointContents( vecOrigin ) != UTIL_PointContents( tr.endpos ) );
+ IRecipientFilter *pFilter;
+ if ( bBroadcast )
+ {
+ // The projectile is going to cross content types
+ // (which will block PVS/PAS). Send to every client
+ pFilter = new CReliableBroadcastRecipientFilter();
+ }
+ else
+ {
+ // just the PVS of where the projectile will hit.
+ pFilter = new CPASFilter( tr.endpos );
+ }
+
+ CEffectData data;
+ data.m_vOrigin = vecOrigin;
+ data.m_vStart = vecVelocity;
+ data.m_fFlags = 6; // Lifetime
+ data.m_nDamageType = 0;
+ if ( bCritical )
+ {
+ data.m_nDamageType |= DMG_CRITICAL;
+ }
+ data.m_CustomColors.m_vecColor1 = vColor1;
+ data.m_CustomColors.m_vecColor2 = vColor2;
+ #ifdef GAME_DLL
+ data.m_nMaterial = pProjectile->GetModelIndex();
+ data.m_nEntIndex = pOwner->entindex();
+ #else
+ data.m_nMaterial = iProjModelIndex;
+ data.m_hEntity = ClientEntityList().EntIndexToHandle( pOwner->entindex() );
+ #endif
+ DispatchEffect( pszDispatchEffect, data );
+ }
+
+ return pProjectile;
+}
+
+const char *CTFBaseProjectile::GetProjectileModelName( void )
+{
+ // should not try to init a base projectile
+ Assert( 0 );
+ return "";
+}
+
+//=============================================================================
+//
+// Client specific functions.
+//
+#ifdef CLIENT_DLL
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFBaseProjectile::PostDataUpdate( DataUpdateType_t type )
+{
+ // Pass through to the base class.
+ BaseClass::PostDataUpdate( type );
+
+ if ( type == DATA_UPDATE_CREATED )
+ {
+ // Now stick our initial velocity and angles into the interpolation history.
+ CInterpolatedVar<Vector> &interpolator = GetOriginInterpolator();
+ interpolator.ClearHistory();
+
+ CInterpolatedVar<QAngle> &rotInterpolator = GetRotationInterpolator();
+ rotInterpolator.ClearHistory();
+
+ float flChangeTime = GetLastChangeTime( LATCH_SIMULATION_VAR );
+
+ // Add a sample 1 second back.
+ Vector vCurOrigin = GetLocalOrigin() - m_vInitialVelocity;
+ interpolator.AddToHead( flChangeTime - 1.0f, &vCurOrigin, false );
+
+ QAngle vCurAngles = GetLocalAngles();
+ rotInterpolator.AddToHead( flChangeTime - 1.0f, &vCurAngles, false );
+
+ // Add the current sample.
+ vCurOrigin = GetLocalOrigin();
+ interpolator.AddToHead( flChangeTime, &vCurOrigin, false );
+
+ rotInterpolator.AddToHead( flChangeTime - 1.0, &vCurAngles, false );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CTFBaseProjectile::DrawModel( int flags )
+{
+ // During the first 0.2 seconds of our life, don't draw ourselves.
+ if ( gpGlobals->curtime - m_flSpawnTime < 0.1f )
+ return 0;
+
+ return BaseClass::DrawModel( flags );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+C_LocalTempEntity *ClientsideProjectileCallback( const CEffectData &data, float flGravityBase, const char *pszParticleName )
+{
+ // Create a nail temp ent, and give it an impact callback to use
+ C_BaseEntity *pEnt = C_BaseEntity::Instance( data.m_hEntity );
+
+ if ( !pEnt || pEnt->IsDormant() )
+ {
+ //Assert( 0 );
+ return NULL;
+ }
+
+ Vector vecSrc = data.m_vOrigin;
+
+ // If we're seeing another player shooting the nails, move their start point to the weapon origin
+ if ( pEnt && pEnt->IsPlayer() )
+ {
+ C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
+ if ( pLocalPlayer != pEnt || C_BasePlayer::ShouldDrawLocalPlayer() )
+ {
+ CTFPlayer *pTFPlayer = ToTFPlayer( pEnt );
+ if ( pTFPlayer->GetActiveWeapon() )
+ {
+ pTFPlayer->GetActiveWeapon()->GetAttachment( "muzzle", vecSrc );
+ }
+ }
+ else
+ {
+ C_BaseEntity *pViewModel = pLocalPlayer->GetViewModel();
+
+ if ( pViewModel )
+ {
+ QAngle vecAngles;
+ Vector vecMuzzleOrigin;
+ int iMuzzleFlashAttachment = pViewModel->LookupAttachment( "muzzle" );
+ pViewModel->GetAttachment( iMuzzleFlashAttachment, vecMuzzleOrigin, vecAngles );
+
+ Vector vForward;
+ AngleVectors( vecAngles, &vForward );
+
+ trace_t trace;
+ UTIL_TraceLine( vecMuzzleOrigin + vForward * -50, vecMuzzleOrigin, MASK_SOLID, pEnt, COLLISION_GROUP_NONE, &trace );
+
+ if ( trace.fraction != 1.0 )
+ {
+ vecSrc = trace.endpos;
+ }
+ }
+ }
+ }
+
+
+ float flGravity = ( flGravityBase * 800 );
+
+ Vector vecGravity(0,0,-flGravity);
+
+ return tempents->ClientProjectile( vecSrc, data.m_vStart, vecGravity, data.m_nMaterial, data.m_fFlags, pEnt, "Impact", pszParticleName );
+}
+
+//=============================================================================
+//
+// Server specific functions.
+//
+#else
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+unsigned int CTFBaseProjectile::PhysicsSolidMaskForEntity( void ) const
+{
+ return BaseClass::PhysicsSolidMaskForEntity() | CONTENTS_HITBOX;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFBaseProjectile::ProjectileTouch( CBaseEntity *pOther )
+{
+ // Verify a correct "other."
+ Assert( pOther );
+ if ( !pOther->IsSolid() || pOther->IsSolidFlagSet( FSOLID_VOLUME_CONTENTS ) )
+ return;
+
+ // Handle hitting skybox (disappear).
+ const trace_t *pTrace = &CBaseEntity::GetTouchTrace();
+ trace_t *pNewTrace = const_cast<trace_t*>( pTrace );
+
+ if( pTrace->surface.flags & SURF_SKY )
+ {
+ UTIL_Remove( this );
+ return;
+ }
+
+ // pass through ladders
+ if( pTrace->surface.flags & CONTENTS_LADDER )
+ return;
+
+ if ( TFGameRules() && TFGameRules()->GameModeUsesUpgrades() )
+ {
+ // Projectile shields
+ if ( InSameTeam( pOther ) && pOther->IsCombatItem() )
+ return;
+ }
+
+ if ( pOther->IsWorld() )
+ {
+ SetAbsVelocity( vec3_origin );
+ AddSolidFlags( FSOLID_NOT_SOLID );
+
+ // Remove immediately. Clientside projectiles will stick in the wall for a bit.
+ UTIL_Remove( this );
+ return;
+ }
+
+ // determine the inflictor, which is the weapon which fired this projectile
+ CBaseEntity *pInflictor = GetLauncher();
+
+ CTakeDamageInfo info;
+ info.SetAttacker( GetOwnerEntity() ); // the player who operated the thing that emitted nails
+ info.SetInflictor( pInflictor ); // the weapon that emitted this projectile
+ info.SetWeapon( pInflictor );
+ info.SetDamage( GetDamage() );
+ info.SetDamageForce( GetDamageForce() );
+ info.SetDamagePosition( GetAbsOrigin() );
+ info.SetDamageType( GetDamageType() );
+
+ Vector dir;
+ AngleVectors( GetAbsAngles(), &dir );
+
+ pOther->DispatchTraceAttack( info, dir, pNewTrace );
+ ApplyMultiDamage();
+
+ if ( pOther && pOther->IsPlayer() )
+ {
+ int iMadMilkSyringes = 0;
+ CALL_ATTRIB_HOOK_INT_ON_OTHER( GetOwnerEntity(), iMadMilkSyringes, mad_milk_syringes );
+ if ( iMadMilkSyringes )
+ {
+ CTFPlayer *pTFVictim = ToTFPlayer( pOther );
+ CTFPlayer *pTFOwner = ToTFPlayer( GetOwnerEntity() );
+ if ( pTFVictim && pTFOwner && pTFVictim->GetTeamNumber() != pTFOwner->GetTeamNumber() )
+ {
+ pTFVictim->m_Shared.AddCond( TF_COND_MAD_MILK, 1.f, pTFOwner );
+ }
+ }
+ }
+
+ UTIL_Remove( this );
+}
+
+Vector CTFBaseProjectile::GetDamageForce( void )
+{
+ Vector vecVelocity = GetAbsVelocity();
+ VectorNormalize( vecVelocity );
+ return (vecVelocity * GetDamage());
+}
+
+void CTFBaseProjectile::FlyThink( void )
+{
+ QAngle angles;
+
+ VectorAngles( GetAbsVelocity(), angles );
+
+ SetAbsAngles( angles );
+
+ SetNextThink( gpGlobals->curtime + 0.1f );
+
+#ifdef _DEBUG
+ if ( tf_debug_projectile.GetBool() )
+ {
+ NDebugOverlay::Box( GetAbsOrigin(), Vector( 0.5, 0.5, 0.5 ), -Vector( 0.5, 0.5, 0.5 ), 0, 255, 0, 100, 0.1 );
+ }
+#endif // _DEBUG
+}
+
+void CTFBaseProjectile::SetScorer( CBaseEntity *pScorer )
+{
+ m_Scorer = pScorer;
+}
+
+CBasePlayer *CTFBaseProjectile::GetScorer( void )
+{
+ return dynamic_cast<CBasePlayer *>( m_Scorer.Get() );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CTFBaseProjectile::GetDamageType( void )
+{
+ Assert( GetWeaponID() != TF_WEAPON_NONE );
+ int iDmgType = g_aWeaponDamageTypes[ GetWeaponID() ];
+ if ( m_bCritical )
+ {
+ iDmgType |= DMG_CRITICAL;
+ }
+ return iDmgType;
+}
+
+#endif