rendered paste bodyBEGIN_DATADESC( CMissile ) DEFINE_FIELD( m_hOwner, FIELD_EHANDLE ), DEFINE_FIELD( m_hRocketTrail, FIELD_EHANDLE ), DEFINE_FIELD( m_flAugerTime, FIELD_TIME ), DEFINE_FIELD( m_flMarkDeadTime, FIELD_TIME ), DEFINE_FIELD( m_flGracePeriodEndsAt, FIELD_TIME ), DEFINE_FIELD( m_flDamage, FIELD_FLOAT ), // Function Pointers DEFINE_FUNCTION( MissileTouch ), DEFINE_FUNCTION( AccelerateThink ), DEFINE_FUNCTION( AugerThink ), DEFINE_FUNCTION( IgniteThink ), DEFINE_FUNCTION( SeekThink ),END_DATADESC()LINK_ENTITY_TO_CLASS( rpg_missile, CMissile );class CWeaponRPG;//-----------------------------------------------------------------------------// Constructor//-----------------------------------------------------------------------------CMissile::CMissile(){ m_hRocketTrail = NULL;}CMissile::~CMissile(){}//-----------------------------------------------------------------------------// Purpose: //////-----------------------------------------------------------------------------void CMissile::Precache( void ){ PrecacheModel( "models/weapons/w_missile.mdl" ); PrecacheModel( "models/weapons/w_missile_launch.mdl" ); PrecacheModel( "models/weapons/w_missile_closed.mdl" );}//-----------------------------------------------------------------------------// Purpose: //////-----------------------------------------------------------------------------void CMissile::Spawn( void ){ Precache(); SetSolid( SOLID_BBOX ); SetModel("models/weapons/w_missile_launch.mdl"); UTIL_SetSize( this, -Vector(4,4,4), Vector(4,4,4) ); SetTouch( &CMissile::MissileTouch ); SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE ); SetThink( &CMissile::IgniteThink ); SetNextThink( gpGlobals->curtime + 0.3f ); m_takedamage = DAMAGE_YES; m_iHealth = m_iMaxHealth = 100; m_bloodColor = DONT_BLEED; m_flGracePeriodEndsAt = 0; AddFlag( FL_OBJECT );}//---------------------------------------------------------//---------------------------------------------------------void CMissile::Event_Killed( const CTakeDamageInfo &info ){ m_takedamage = DAMAGE_NO; ShotDown();}unsigned int CMissile::PhysicsSolidMaskForEntity( void ) const{ return BaseClass::PhysicsSolidMaskForEntity() | CONTENTS_HITBOX;}//---------------------------------------------------------//---------------------------------------------------------int CMissile::OnTakeDamage_Alive( const CTakeDamageInfo &info ){ if ( ( info.GetDamageType() & (DMG_MISSILEDEFENSE | DMG_AIRBOAT) ) == false ) return 0; bool bIsDamaged; if( m_iHealth <= AugerHealth() ) { // This missile is already damaged (i.e., already running AugerThink) bIsDamaged = true; } else { // This missile isn't damaged enough to wobble in flight yet bIsDamaged = false; } int nRetVal = BaseClass::OnTakeDamage_Alive( info ); if( !bIsDamaged ) { if ( m_iHealth <= AugerHealth() ) { ShotDown(); } } return nRetVal;}//-----------------------------------------------------------------------------// Purpose: Stops any kind of tracking and shoots dumb//-----------------------------------------------------------------------------void CMissile::DumbFire( void ){ SetThink( NULL ); SetMoveType( MOVETYPE_FLY ); SetModel("models/weapons/w_missile.mdl"); UTIL_SetSize( this, vec3_origin, vec3_origin ); EmitSound( "Missile.Ignite" ); // Smoke trail. CreateSmokeTrail();}//-----------------------------------------------------------------------------// Purpose: //-----------------------------------------------------------------------------void CMissile::SetGracePeriod( float flGracePeriod ){ m_flGracePeriodEndsAt = gpGlobals->curtime + flGracePeriod; // Go non-solid until the grace period ends AddSolidFlags( FSOLID_NOT_SOLID );}//---------------------------------------------------------//---------------------------------------------------------void CMissile::AccelerateThink( void ){ Vector vecForward; // !!!UNDONE - make this work exactly the same as HL1 RPG, lest we have looping sound bugs again! EmitSound( "Missile.Accelerate" ); // SetEffects( EF_LIGHT ); AngleVectors( GetLocalAngles(), &vecForward ); SetAbsVelocity( vecForward * RPG_SPEED ); SetThink( &CMissile::SeekThink ); SetNextThink( gpGlobals->curtime + 0.1f );}#define AUGER_YDEVIANCE 20.0f#define AUGER_XDEVIANCEUP 8.0f#define AUGER_XDEVIANCEDOWN 1.0f//---------------------------------------------------------//---------------------------------------------------------void CMissile::AugerThink( void ){ // If we've augered long enough, then just explode if ( m_flAugerTime < gpGlobals->curtime ) { Explode(); return; } if ( m_flMarkDeadTime < gpGlobals->curtime ) { m_lifeState = LIFE_DYING; } QAngle angles = GetLocalAngles(); angles.y += random->RandomFloat( -AUGER_YDEVIANCE, AUGER_YDEVIANCE ); angles.x += random->RandomFloat( -AUGER_XDEVIANCEDOWN, AUGER_XDEVIANCEUP ); SetLocalAngles( angles ); Vector vecForward; AngleVectors( GetLocalAngles(), &vecForward ); SetAbsVelocity( vecForward * 1000.0f ); SetNextThink( gpGlobals->curtime + 0.05f );}//-----------------------------------------------------------------------------// Purpose: Causes the missile to spiral to the ground and explode, due to damage//-----------------------------------------------------------------------------void CMissile::ShotDown( void ){ CEffectData data; data.m_vOrigin = GetAbsOrigin(); DispatchEffect( "RPGShotDown", data ); if ( m_hRocketTrail != NULL ) { m_hRocketTrail->m_bDamaged = true; } SetThink( &CMissile::AugerThink ); SetNextThink( gpGlobals->curtime ); m_flAugerTime = gpGlobals->curtime + 1.5f; m_flMarkDeadTime = gpGlobals->curtime + 0.75; // Let the RPG start reloading immediately if ( m_hOwner != NULL ) { m_hOwner->NotifyRocketDied(); m_hOwner = NULL; }}//-----------------------------------------------------------------------------// The actual explosion //-----------------------------------------------------------------------------void CMissile::DoExplosion( void ){ // Explode ExplosionCreate( GetAbsOrigin(), GetAbsAngles(), GetOwnerEntity(), GetDamage(), GetDamage() * 2, SF_ENVEXPLOSION_NOSPARKS | SF_ENVEXPLOSION_NODLIGHTS | SF_ENVEXPLOSION_NOSMOKE, 0.0f, this);}//-----------------------------------------------------------------------------// Purpose: //-----------------------------------------------------------------------------void CMissile::Explode( void ){ // Don't explode against the skybox. Just pretend that // the missile flies off into the distance. Vector forward; GetVectors( &forward, NULL, NULL ); trace_t tr; UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + forward * 16, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr ); m_takedamage = DAMAGE_NO; SetSolid( SOLID_NONE ); if( tr.fraction == 1.0 || !(tr.surface.flags & SURF_SKY) ) { DoExplosion(); } if( m_hRocketTrail ) { m_hRocketTrail->SetLifetime(0.1f); m_hRocketTrail = NULL; } if ( m_hOwner != NULL ) { m_hOwner->NotifyRocketDied(); m_hOwner = NULL; } StopSound( "Missile.Ignite" ); UTIL_Remove( this );}//-----------------------------------------------------------------------------// Purpose: // Input : *pOther - //-----------------------------------------------------------------------------void CMissile::MissileTouch( CBaseEntity *pOther ){ Assert( pOther ); // Don't touch triggers (but DO hit weapons) if ( pOther->IsSolidFlagSet(FSOLID_TRIGGER|FSOLID_VOLUME_CONTENTS) && pOther->GetCollisionGroup() != COLLISION_GROUP_WEAPON ) return; Explode();}//-----------------------------------------------------------------------------// Purpose: //-----------------------------------------------------------------------------void CMissile::CreateSmokeTrail( void ){ if ( m_hRocketTrail ) return; // Smoke trail. if ( (m_hRocketTrail = RocketTrail::CreateRocketTrail()) != NULL ) { m_hRocketTrail->m_Opacity = 0.2f; m_hRocketTrail->m_SpawnRate = 100; m_hRocketTrail->m_ParticleLifetime = 0.5f; m_hRocketTrail->m_StartColor.Init( 0.65f, 0.65f , 0.65f ); m_hRocketTrail->m_EndColor.Init( 0.0, 0.0, 0.0 ); m_hRocketTrail->m_StartSize = 8; m_hRocketTrail->m_EndSize = 32; m_hRocketTrail->m_SpawnRadius = 4; m_hRocketTrail->m_MinSpeed = 2; m_hRocketTrail->m_MaxSpeed = 16; m_hRocketTrail->SetLifetime( 999 ); m_hRocketTrail->FollowEntity( this, "0" ); }}//-----------------------------------------------------------------------------// Purpose: //-----------------------------------------------------------------------------void CMissile::IgniteThink( void ){ SetMoveType( MOVETYPE_FLY ); SetModel("models/weapons/w_missile.mdl"); UTIL_SetSize( this, vec3_origin, vec3_origin ); RemoveSolidFlags( FSOLID_NOT_SOLID ); //TODO: Play opening sound Vector vecForward; EmitSound( "Missile.Ignite" ); AngleVectors( GetLocalAngles(), &vecForward ); SetAbsVelocity( vecForward * RPG_SPEED ); SetThink( &CMissile::SeekThink ); SetNextThink( gpGlobals->curtime ); if ( m_hOwner && m_hOwner->GetOwner() ) { CBasePlayer *pPlayer = ToBasePlayer( m_hOwner->GetOwner() ); color32 white = { 255,225,205,64 }; UTIL_ScreenFade( pPlayer, white, 0.1f, 0.0f, FFADE_IN ); } CreateSmokeTrail();}//-----------------------------------------------------------------------------// Gets the shooting position //-----------------------------------------------------------------------------void CMissile::GetShootPosition( CLaserDot *pLaserDot, Vector *pShootPosition ){ if ( pLaserDot->GetOwnerEntity() != NULL ) { //FIXME: Do we care this isn't exactly the muzzle position? *pShootPosition = pLaserDot->GetOwnerEntity()->WorldSpaceCenter(); } else { *pShootPosition = pLaserDot->GetChasePosition(); }} //-----------------------------------------------------------------------------// Purpose: //-----------------------------------------------------------------------------#define RPG_HOMING_SPEED 0.125fvoid CMissile::ComputeActualDotPosition( CLaserDot *pLaserDot, Vector *pActualDotPosition, float *pHomingSpeed ){ *pHomingSpeed = RPG_HOMING_SPEED; if ( pLaserDot->GetTargetEntity() ) { *pActualDotPosition = pLaserDot->GetChasePosition(); return; } Vector vLaserStart; GetShootPosition( pLaserDot, &vLaserStart ); //Get the laser's vector Vector vLaserDir; VectorSubtract( pLaserDot->GetChasePosition(), vLaserStart, vLaserDir ); //Find the length of the current laser float flLaserLength = VectorNormalize( vLaserDir ); //Find the length from the missile to the laser's owner float flMissileLength = GetAbsOrigin().DistTo( vLaserStart ); //Find the length from the missile to the laser's position Vector vecTargetToMissile; VectorSubtract( GetAbsOrigin(), pLaserDot->GetChasePosition(), vecTargetToMissile ); float flTargetLength = VectorNormalize( vecTargetToMissile ); // See if we should chase the line segment nearest us if ( ( flMissileLength < flLaserLength ) || ( flTargetLength <= 512.0f ) ) { *pActualDotPosition = UTIL_PointOnLineNearestPoint( vLaserStart, pLaserDot->GetChasePosition(), GetAbsOrigin() ); *pActualDotPosition += ( vLaserDir * 256.0f ); } else { // Otherwise chase the dot *pActualDotPosition = pLaserDot->GetChasePosition(); }// NDebugOverlay::Line( pLaserDot->GetChasePosition(), vLaserStart, 0, 255, 0, true, 0.05f );// NDebugOverlay::Line( GetAbsOrigin(), *pActualDotPosition, 255, 0, 0, true, 0.05f );// NDebugOverlay::Cross3D( *pActualDotPosition, -Vector(4,4,4), Vector(4,4,4), 255, 0, 0, true, 0.05f );}//-----------------------------------------------------------------------------// Purpose: //-----------------------------------------------------------------------------void CMissile::SeekThink( void ){ CBaseEntity *pBestDot = NULL; float flBestDist = MAX_TRACE_LENGTH; float dotDist; // If we have a grace period, go solid when it ends if ( m_flGracePeriodEndsAt ) { if ( m_flGracePeriodEndsAt < gpGlobals->curtime ) { RemoveSolidFlags( FSOLID_NOT_SOLID ); m_flGracePeriodEndsAt = 0; } } //Search for all dots relevant to us for( CLaserDot *pEnt = GetLaserDotList(); pEnt != NULL; pEnt = pEnt->m_pNext ) { if ( !pEnt->IsOn() ) continue; if ( pEnt->GetOwnerEntity() != GetOwnerEntity() ) continue; dotDist = (GetAbsOrigin() - pEnt->GetAbsOrigin()).Length(); //Find closest if ( dotDist < flBestDist ) { pBestDot = pEnt; flBestDist = dotDist; } } //If we have a dot target if ( pBestDot == NULL ) { //Think as soon as possible SetNextThink( gpGlobals->curtime ); return; } CLaserDot *pLaserDot = (CLaserDot *)pBestDot; Vector targetPos; float flHomingSpeed; Vector vecLaserDotPosition; ComputeActualDotPosition( pLaserDot, &targetPos, &flHomingSpeed ); if ( IsSimulatingOnAlternateTicks() ) flHomingSpeed *= 2; Vector vTargetDir; VectorSubtract( targetPos, GetAbsOrigin(), vTargetDir ); float flDist = VectorNormalize( vTargetDir ); Vector vDir = GetAbsVelocity(); float flSpeed = VectorNormalize( vDir ); Vector vNewVelocity = vDir; if ( gpGlobals->frametime > 0.0f ) { if ( flSpeed != 0 ) { vNewVelocity = ( flHomingSpeed * vTargetDir ) + ( ( 1 - flHomingSpeed ) * vDir ); // This computation may happen to cancel itself out exactly. If so, slam to targetdir. if ( VectorNormalize( vNewVelocity ) < 1e-3 ) { vNewVelocity = (flDist != 0) ? vTargetDir : vDir; } } else { vNewVelocity = vTargetDir; } } QAngle finalAngles; VectorAngles( vNewVelocity, finalAngles ); SetAbsAngles( finalAngles ); vNewVelocity *= flSpeed; SetAbsVelocity( vNewVelocity ); if( GetAbsVelocity() == vec3_origin ) { // Strange circumstances have brought this missile to halt. Just blow it up. Explode(); return; } // Think as soon as possible SetNextThink( gpGlobals->curtime );}//-----------------------------------------------------------------------------// Purpose: //// Input : &vecOrigin - // &vecAngles - // NULL - //// Output : CMissile//-----------------------------------------------------------------------------CMissile *CMissile::Create( const Vector &vecOrigin, const QAngle &vecAngles, edict_t *pentOwner = NULL ){ //CMissile *pMissile = (CMissile *)CreateEntityByName("rpg_missile" ); CMissile *pMissile = (CMissile *) CBaseEntity::Create( "rpg_missile", vecOrigin, vecAngles, CBaseEntity::Instance( pentOwner ) ); pMissile->SetOwnerEntity( Instance( pentOwner ) ); pMissile->Spawn(); pMissile->AddEffects( EF_NOSHADOW ); Vector vecForward; AngleVectors( vecAngles, &vecForward ); pMissile->SetAbsVelocity( vecForward * 300 + Vector( 0,0, 128 ) ); return pMissile;}