//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. =======// // // Purpose: TF Base Rockets. // //=============================================================================// #include "cbase.h" #include "tf_weaponbase_rocket.h" // Server specific. #ifdef GAME_DLL #include "soundent.h" #include "te_effect_dispatch.h" #include "tf_fx.h" #include "iscorer.h" 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 //============================================================================= // // TF Base Rocket tables. // IMPLEMENT_NETWORKCLASS_ALIASED( TFBaseRocket, DT_TFBaseRocket ) BEGIN_NETWORK_TABLE( CTFBaseRocket, DT_TFBaseRocket ) // Client specific. #ifdef CLIENT_DLL RecvPropVector( RECVINFO( m_vInitialVelocity ) ), RecvPropVector( RECVINFO_NAME( m_vecNetworkOrigin, m_vecOrigin ) ), RecvPropQAngles( RECVINFO_NAME( m_angNetworkAngles, m_angRotation ) ), // Server specific. #else SendPropVector( SENDINFO( m_vInitialVelocity ), 12 /*nbits*/, 0 /*flags*/, -3000 /*low value*/, 3000 /*high value*/ ), 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 ), #endif END_NETWORK_TABLE() // Server specific. #ifdef GAME_DLL BEGIN_DATADESC( CTFBaseRocket ) DEFINE_ENTITYFUNC( RocketTouch ), DEFINE_THINKFUNC( FlyThink ), END_DATADESC() #endif ConVar tf_rocket_show_radius( "tf_rocket_show_radius", "0", FCVAR_REPLICATED | FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY, "Render rocket radius." ); //============================================================================= // // Shared (client/server) functions. // //----------------------------------------------------------------------------- // Purpose: Constructor. //----------------------------------------------------------------------------- CTFBaseRocket::CTFBaseRocket() { m_vInitialVelocity.Init(); // Client specific. #ifdef CLIENT_DLL m_flSpawnTime = 0.0f; // Server specific. #else m_flDamage = 0.0f; #endif } //----------------------------------------------------------------------------- // Purpose: Destructor. //----------------------------------------------------------------------------- CTFBaseRocket::~CTFBaseRocket() { } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFBaseRocket::Precache( void ) { BaseClass::Precache(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFBaseRocket::Spawn( void ) { // Precache. Precache(); // Client specific. #ifdef CLIENT_DLL m_flSpawnTime = gpGlobals->curtime; BaseClass::Spawn(); // Server specific. #else //Derived classes must have set model. Assert( GetModel() ); SetSolid( SOLID_BBOX ); SetMoveType( MOVETYPE_FLY, MOVECOLLIDE_FLY_CUSTOM ); AddEFlags( EFL_NO_WATER_VELOCITY_CHANGE ); AddEffects( EF_NOSHADOW ); SetCollisionGroup( TFCOLLISION_GROUP_ROCKETS ); UTIL_SetSize( this, -Vector( 0, 0, 0 ), Vector( 0, 0, 0 ) ); // Setup attributes. m_takedamage = DAMAGE_NO; SetGravity( 0.0f ); // Setup the touch and think functions. SetTouch( &CTFBaseRocket::RocketTouch ); SetThink( &CTFBaseRocket::FlyThink ); SetNextThink( gpGlobals->curtime ); // Don't collide with players on the owner's team for the first bit of our life m_flCollideWithTeammatesTime = gpGlobals->curtime + 0.25; m_bCollideWithTeammates = false; #endif } //============================================================================= // // Client specific functions. // #ifdef CLIENT_DLL //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFBaseRocket::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 &interpolator = GetOriginInterpolator(); interpolator.ClearHistory(); CInterpolatedVar &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 CTFBaseRocket::DrawModel( int flags ) { // During the first 0.2 seconds of our life, don't draw ourselves. if ( gpGlobals->curtime - m_flSpawnTime < 0.2f ) return 0; return BaseClass::DrawModel( flags ); } //============================================================================= // // Server specific functions. // #else //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CTFBaseRocket *CTFBaseRocket::Create( const char *pszClassname, const Vector &vecOrigin, const QAngle &vecAngles, CBaseEntity *pOwner ) { CTFBaseRocket *pRocket = static_cast( CBaseEntity::Create( pszClassname, vecOrigin, vecAngles, pOwner ) ); if ( !pRocket ) return NULL; // Initialize the owner. pRocket->SetOwnerEntity( pOwner ); // Spawn. pRocket->Spawn(); // Setup the initial velocity. Vector vecForward, vecRight, vecUp; AngleVectors( vecAngles, &vecForward, &vecRight, &vecUp ); Vector vecVelocity = vecForward * 1100.0f; pRocket->SetAbsVelocity( vecVelocity ); pRocket->SetupInitialTransmittedGrenadeVelocity( vecVelocity ); // Setup the initial angles. QAngle angles; VectorAngles( vecVelocity, angles ); pRocket->SetAbsAngles( angles ); // Set team. pRocket->ChangeTeam( pOwner->GetTeamNumber() ); return pRocket; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFBaseRocket::RocketTouch( CBaseEntity *pOther ) { // Verify a correct "other." Assert( pOther ); if ( pOther->IsSolidFlagSet( FSOLID_TRIGGER | FSOLID_VOLUME_CONTENTS ) ) return; // Handle hitting skybox (disappear). const trace_t *pTrace = &CBaseEntity::GetTouchTrace(); if( pTrace->surface.flags & SURF_SKY ) { UTIL_Remove( this ); return; } trace_t trace; memcpy( &trace, pTrace, sizeof( trace_t ) ); Explode( &trace, pOther ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- unsigned int CTFBaseRocket::PhysicsSolidMaskForEntity( void ) const { int teamContents = 0; if ( m_bCollideWithTeammates == false ) { // Only collide with the other team teamContents = ( GetTeamNumber() == TF_TEAM_RED ) ? CONTENTS_BLUETEAM : CONTENTS_REDTEAM; } else { // Collide with both teams teamContents = CONTENTS_REDTEAM | CONTENTS_BLUETEAM; } return BaseClass::PhysicsSolidMaskForEntity() | teamContents; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFBaseRocket::Explode( trace_t *pTrace, CBaseEntity *pOther ) { // Save this entity as enemy, they will take 100% damage. m_hEnemy = pOther; // Invisible. SetModelName( NULL_STRING ); AddSolidFlags( FSOLID_NOT_SOLID ); m_takedamage = DAMAGE_NO; // Pull out a bit. if ( pTrace->fraction != 1.0 ) { SetAbsOrigin( pTrace->endpos + ( pTrace->plane.normal * 1.0f ) ); } // Play explosion sound and effect. Vector vecOrigin = GetAbsOrigin(); CPVSFilter filter( vecOrigin ); TE_TFExplosion( filter, 0.0f, vecOrigin, pTrace->plane.normal, GetWeaponID(), pOther->entindex() ); CSoundEnt::InsertSound ( SOUND_COMBAT, vecOrigin, 1024, 3.0 ); // Damage. CBaseEntity *pAttacker = GetOwnerEntity(); IScorer *pScorerInterface = dynamic_cast( pAttacker ); if ( pScorerInterface ) { pAttacker = pScorerInterface->GetScorer(); } CTakeDamageInfo info( this, pAttacker, vec3_origin, vecOrigin, GetDamage(), GetDamageType() ); float flRadius = GetRadius(); RadiusDamage( info, vecOrigin, flRadius, CLASS_NONE, NULL ); // Debug! if ( tf_rocket_show_radius.GetBool() ) { DrawRadius( flRadius ); } // Don't decal players with scorch. if ( !pOther->IsPlayer() ) { UTIL_DecalTrace( pTrace, "Scorch" ); } // Remove the rocket. UTIL_Remove( this ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFBaseRocket::DrawRadius( float flRadius ) { Vector pos = GetAbsOrigin(); int r = 255; int g = 0, b = 0; float flLifetime = 10.0f; 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( angle ) + pos.x; edge.y = pos.y; edge.z = flRadius * sin( 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( angle ) + pos.y; edge.z = flRadius * sin( 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( angle ) + pos.x; edge.y = flRadius * sin( angle ) + pos.y; edge.z = pos.z; NDebugOverlay::Line( edge, lastEdge, r, g, b, !bDepthTest, flLifetime ); lastEdge = edge; } } void CTFBaseRocket::FlyThink( void ) { if ( gpGlobals->curtime > m_flCollideWithTeammatesTime && m_bCollideWithTeammates == false ) { m_bCollideWithTeammates = true; } SetNextThink( gpGlobals->curtime + 0.1 ); } #endif