//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: Rockets (Weapon) // //=============================================================================// #include "cbase.h" #include "weapon_grenade_rocket.h" #if defined( CLIENT_DLL ) // Client Only #include "hud.h" #include "particles_simple.h" #else // Server Only #include "gameinterface.h" #include "engine/IEngineSound.h" #include "explode.h" #include "tf_team.h" #include "env_laserdesignation.h" #include "iservervehicle.h" #endif LINK_ENTITY_TO_CLASS( weapon_grenade_rocket, CWeaponGrenadeRocket ); BEGIN_PREDICTION_DATA( CWeaponGrenadeRocket ) END_PREDICTION_DATA() IMPLEMENT_NETWORKCLASS_ALIASED( WeaponGrenadeRocket, DT_WeaponGrenadeRocket ) BEGIN_NETWORK_TABLE( CWeaponGrenadeRocket, DT_WeaponGrenadeRocket ) //#if !defined( CLIENT_DLL ) //#else //#endif END_NETWORK_TABLE() #define WEAPON_GRENADE_ROCKET_VELOCITY 1000 #if !defined( CLIENT_DLL ) // Server Only ConVar weapon_grenade_rocket_track_range_mod( "weapon_grenade_rocket_track_range_mod","1.5", FCVAR_NONE, "Range multiplier when a rocket's tracking a designated target." ); ConVar weapon_grenade_rocket_force( "weapon_grenade_rocket_force","150.0", FCVAR_NONE, "Rocket force modifier." ); #endif //----------------------------------------------------------------------------- // Purpose: Constructor //----------------------------------------------------------------------------- CWeaponGrenadeRocket::CWeaponGrenadeRocket() { m_flDamage = 100.0f; #if !defined( CLIENT_DLL ) // Server Only UseClientSideAnimation(); #endif } //----------------------------------------------------------------------------- // Purpose: Deconstructor //----------------------------------------------------------------------------- CWeaponGrenadeRocket::~CWeaponGrenadeRocket() { #if defined( CLIENT_DLL ) StopSound( entindex(), "GrenadeRocket.FlyLoop" ); #endif } //----------------------------------------------------------------------------- // Purpose: Create a weapon grenade rocket //----------------------------------------------------------------------------- CWeaponGrenadeRocket *CWeaponGrenadeRocket::Create( const Vector &vecOrigin, const Vector &vecForward, float flMaxRange, CBaseEntity *pOwner ) { #if !defined( CLIENT_DLL ) CWeaponGrenadeRocket *pRocket = ( CWeaponGrenadeRocket* )CreateEntityByName( "weapon_grenade_rocket" ); UTIL_SetOrigin( pRocket, vecOrigin ); QAngle angles; VectorAngles( vecForward, angles ); pRocket->SetLocalAngles( angles ); pRocket->Spawn(); pRocket->SetOwnerEntity( pOwner ); pRocket->ChangeTeam( pOwner->GetTeamNumber() ); pRocket->SetMaxRange( flMaxRange ); return pRocket; #else return NULL; #endif } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CWeaponGrenadeRocket::SetMaxRange( float flRange ) { m_flMaxRange = flRange; m_flFallingSpeed = 200; // Initial falling speed // Reduce max range for upward shots Vector vecForward; GetVectors( &vecForward, NULL, NULL ); if ( vecForward.z > 0 ) { m_flMaxRange = MAX(1, m_flMaxRange - (vecForward.z * 1200)); } else { m_flMaxRange -= (vecForward.z * 2400); } #if !defined( CLIENT_DLL ) if ( m_flMaxRange ) { float flSpeed = GetLocalVelocity().Length(); Assert( flSpeed ); m_flExceedRangeTime = gpGlobals->curtime + (m_flMaxRange / flSpeed); // Start looking for designators SetThink( TrackThink ); SetNextThink( gpGlobals->curtime + 0.1f ); } #endif } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CWeaponGrenadeRocket::Spawn( void ) { #if !defined( CLIENT_DLL ) Precache(); m_flRadius = 100; SetMoveType( MOVETYPE_FLY ); SetSolid( SOLID_BBOX ); SetModel( "models/weapons/w_missile.mdl" ); UTIL_SetSize( this, vec3_origin, vec3_origin ); SetCollisionGroup( TFCOLLISION_GROUP_WEAPON ); // Forward! Vector forward; AngleVectors( GetLocalAngles(), &forward, NULL, NULL ); SetAbsVelocity( forward * WEAPON_GRENADE_ROCKET_VELOCITY ); SetTouch( RocketTouch ); #else // Start our flying sound loop CPASAttenuationFilter filter( this ); filter.MakeReliable(); EmitSound( filter, entindex(), "GrenadeRocket.FlyLoop" ); #endif } #if !defined( CLIENT_DLL ) // Server Only //----------------------------------------------------------------------------- // Purpose: Return my owner as my scorer //----------------------------------------------------------------------------- CBasePlayer *CWeaponGrenadeRocket::GetScorer( void ) { return ToBasePlayer( m_hOwner ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CWeaponGrenadeRocket::Precache( void ) { PrecacheModel( "models/weapons/w_missile.mdl" ); PrecacheScriptSound( "GrenadeRocket.FlyLoop" ); } //----------------------------------------------------------------------------- // Purpose: We've exceeded this rocket's range, start heading downward, randomly //----------------------------------------------------------------------------- void CWeaponGrenadeRocket::ExceededRangeThink( void ) { Vector vecZ( 0,0,1 ); Vector vecPerp; // Weave drunkely and head down Vector vecVelocity = GetLocalVelocity(); CrossProduct( vecVelocity, vecZ, vecPerp ); VectorNormalize( vecPerp ); vecPerp *= random->RandomFloat(-100,100); vecVelocity += vecPerp; vecVelocity.z -= m_flFallingSpeed; m_flFallingSpeed += 50; SetLocalVelocity( vecVelocity ); SetAnglesToMatchVelocity(); SetNextThink( gpGlobals->curtime + 0.3 ); } //----------------------------------------------------------------------------- // Purpose: Track towards my my designated target //----------------------------------------------------------------------------- void CWeaponGrenadeRocket::TrackThink( void ) { SetNextThink( gpGlobals->curtime + 0.3 ); // Have I exceeded my range? if ( gpGlobals->curtime > m_flExceedRangeTime ) { SetThink( ExceededRangeThink ); // Start falling immediately ExceededRangeThink(); return; } // Look for any laser designators in tracking view int iTeam = GetTeamNumber(); int iCount = CEnvLaserDesignation::GetNumLaserDesignators( iTeam ); if ( !iCount ) return; // Get the potential lock range float flIncreasedMaxRange = m_flMaxRange * weapon_grenade_rocket_track_range_mod.GetFloat(); float flNearestDot = 0.95; Vector vecNearestTarget = vec3_origin; bool bFoundOne = false; // Any valid designated targets? for ( int i = 0; i < iCount; i++ ) { Vector vecTarget; if ( !CEnvLaserDesignation::GetLaserDesignation( iTeam, i, &vecTarget ) ) continue; // Check validity of designated target Vector vecToTarget = ( vecTarget - GetAbsOrigin() ); float flDistanceSqr = vecToTarget.LengthSqr(); // Make sure it's not too far if ( flDistanceSqr > (flIncreasedMaxRange*flIncreasedMaxRange) ) continue; // Make sure it's near my flight path VectorNormalize(vecToTarget); Vector vecForward; GetVectors( &vecForward, NULL, NULL ); float flDot = DotProduct( vecToTarget, vecForward ); if ( flDot < flNearestDot ) continue; flNearestDot = flDot; vecNearestTarget = vecTarget; bFoundOne = true; } // No valid targets if ( !bFoundOne ) return; SetNextThink( gpGlobals->curtime + 0.1f ); // Turn towards my target Vector vecToTarget = (vecNearestTarget - GetAbsOrigin()); VectorNormalize(vecToTarget); // Shamelessly ripped from HL1 RPG float flSpeed = GetAbsVelocity().Length(); SetAbsVelocity( (GetAbsVelocity() * 0.5) + (vecToTarget * flSpeed * 0.498) ); SetAnglesToMatchVelocity(); } //----------------------------------------------------------------------------- // Purpose: Set angles to match our velocity //----------------------------------------------------------------------------- void CWeaponGrenadeRocket::SetAnglesToMatchVelocity( void ) { QAngle angles; VectorAngles( GetAbsVelocity(), angles ); SetLocalAngles( angles ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CWeaponGrenadeRocket::RocketTouch( CBaseEntity *pOther ) { Assert( pOther ); if ( !pOther->IsSolid() ) return; // Apply forces to vehicles. if ( pOther->GetServerVehicle() ) { ApplyForcesToVehicle( pOther ); } CPASFilter filter( GetAbsOrigin() ); te->Explosion( filter, 0.0, &GetAbsOrigin(), g_sModelIndexFireball, 2.0, 15, TE_EXPLFLAG_NONE, 100, m_flDamage ); // Use the owner's position as the reported position Vector vecReported = vec3_origin; if ( GetOwnerEntity() ) { vecReported = GetOwnerEntity()->GetAbsOrigin(); } RadiusDamage( CTakeDamageInfo( this, GetOwnerEntity(), vec3_origin, GetAbsOrigin(), GetDamage(), GetDamageType(), 0, &vecReported ), GetAbsOrigin(), GetDamageRadius(), CLASS_NONE, NULL ); UTIL_Remove( this ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CWeaponGrenadeRocket::ApplyForcesToVehicle( CBaseEntity *pEntity ) { // Check team - don't apply forces to our own team's vehicles. if ( pEntity->GetTeam() == GetTeam() ) return; IServerVehicle *pVehicle = pEntity->GetServerVehicle(); if ( !pVehicle ) return; IPhysicsObject *pPhysObject = pEntity->VPhysicsGetObject(); if ( pPhysObject ) { //------------------------------------------------------------ // Rocket the vehicle in the direction of the incoming rocket. //------------------------------------------------------------ Vector vecForceDir = GetAbsVelocity(); vecForceDir.z = 0.0f; VectorNormalize( vecForceDir ); float flForce = pPhysObject->GetMass(); flForce += ( 4.0f * 100.0f ); // Wheels flForce *= weapon_grenade_rocket_force.GetFloat(); vecForceDir *= flForce; pPhysObject->ApplyForceOffset( vecForceDir, GetAbsOrigin() ); } } #else // Client Only //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CWeaponGrenadeRocket::OnDataChanged( DataUpdateType_t updateType ) { BaseClass::OnDataChanged( updateType ); // Only think when "rocketing." SetNextClientThink( CLIENT_THINK_ALWAYS ); } //----------------------------------------------------------------------------- // Purpose: Spawn rocket effects! //----------------------------------------------------------------------------- void CWeaponGrenadeRocket::ClientThink( void ) { // Fire smoke puffs out the side CSmartPtr pSmokeEmitter = CSimpleEmitter::Create( "C_GrenadeRocket::Effect" ); pSmokeEmitter->SetSortOrigin( GetAbsOrigin() ); PMaterialHandle hSphereMaterial = pSmokeEmitter->GetPMaterial( "particle/particle_noisesphere" ); int iSmokeClouds = random->RandomInt( 1,2 ); for ( int i = 0; i < iSmokeClouds; i++ ) { SimpleParticle *pParticle = ( SimpleParticle* ) pSmokeEmitter->AddParticle( sizeof( SimpleParticle ), hSphereMaterial, GetAbsOrigin() ); if ( !pParticle ) return; // Particle data. pParticle->m_flLifetime = 0.0f; pParticle->m_flDieTime = random->RandomFloat( 0.1f, 0.3f ); pParticle->m_uchStartSize = 10; pParticle->m_uchEndSize = pParticle->m_uchStartSize + 2; pParticle->m_vecVelocity = GetAbsVelocity(); pParticle->m_uchStartAlpha = 255; pParticle->m_uchEndAlpha = 64; pParticle->m_flRoll = random->RandomFloat( 180, 360 ); pParticle->m_flRollDelta = random->RandomFloat( -1, 1 ); pParticle->m_uchColor[0] = 50; pParticle->m_uchColor[1] = 250; pParticle->m_uchColor[2] = 50; } } #endif