//========= Copyright Valve Corporation, All rights reserved. ============// // // TF Energy Ball // //============================================================================= #include "cbase.h" #include "tf_projectile_energy_ball.h" #include "soundent.h" #include "tf_fx.h" #include "props.h" #include "baseobject_shared.h" #include "SpriteTrail.h" #include "IEffects.h" #include "te_effect_dispatch.h" #include "collisionutils.h" #include "bone_setup.h" #include "decals.h" #include "tf_player.h" #include "te_effect_dispatch.h" #include "tf_gamerules.h" #include "tf_weapon_rocketlauncher.h" //============================================================================= // // TF Energy Ball Projectile functions (Server specific). // #define ENERGY_BALL_MODEL "models/weapons/w_models/w_drg_ball.mdl" #define ARROW_GRAVITY 0.3f #define ENERGY_BALL_THINK_CONTEXT "CTFProjectile_EnergyBallThink" //----------------------------------------------------------------------------- LINK_ENTITY_TO_CLASS( tf_projectile_energy_ball, CTFProjectile_EnergyBall ); PRECACHE_WEAPON_REGISTER( tf_projectile_energy_ball ); IMPLEMENT_NETWORKCLASS_ALIASED( TFProjectile_EnergyBall, DT_TFProjectile_EnergyBall ) BEGIN_NETWORK_TABLE( CTFProjectile_EnergyBall, DT_TFProjectile_EnergyBall ) SendPropBool( SENDINFO( m_bChargedShot ) ), SendPropVector( SENDINFO( m_vColor1 ), 8, 0, 0, 1 ), SendPropVector( SENDINFO( m_vColor2 ), 8, 0, 0, 1 ) END_NETWORK_TABLE() BEGIN_DATADESC( CTFProjectile_EnergyBall ) //DEFINE_THINKFUNC( ImpactThink ), END_DATADESC() //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CTFProjectile_EnergyBall::CTFProjectile_EnergyBall() { m_bChargedShot = false; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CTFProjectile_EnergyBall::~CTFProjectile_EnergyBall() { } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CTFProjectile_EnergyBall *CTFProjectile_EnergyBall::Create( const Vector &vecOrigin, const QAngle &vecAngles, const float fSpeed, const float fGravity, CBaseEntity *pOwner, CBaseEntity *pScorer ) { CTFProjectile_EnergyBall *pBall = static_cast( CBaseEntity::Create( "tf_projectile_energy_ball", vecOrigin, vecAngles, pOwner ) ); if ( pBall ) { pBall->InitEnergyBall( vecOrigin, vecAngles, fSpeed, fGravity, pOwner, pScorer ); } return pBall; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFProjectile_EnergyBall::InitEnergyBall( const Vector &vecOrigin, const QAngle &vecAngles, const float fSpeed, const float fGravity, CBaseEntity *pOwner, CBaseEntity *pScorer ) { // Initialize the owner. SetOwnerEntity( pOwner ); // Set team. ChangeTeam( pOwner->GetTeamNumber() ); // Spawn. Spawn(); SetGravity( fGravity ); SetCritical( true ); // Setup the initial velocity. Vector vecForward, vecRight, vecUp; AngleVectors( vecAngles, &vecForward, &vecRight, &vecUp ); Vector vecVelocity = vecForward * fSpeed; SetAbsVelocity( vecVelocity ); SetupInitialTransmittedGrenadeVelocity( vecVelocity ); // Setup the initial angles. QAngle angles; VectorAngles( vecVelocity, angles ); SetAbsAngles( angles ); // Save the scoring player. SetScorer( pScorer ); if ( pScorer ) { SetTruceValidForEnt( pScorer->IsTruceValidForEnt() ); } m_flInitTime = gpGlobals->curtime; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFProjectile_EnergyBall::Spawn() { SetModel( ENERGY_BALL_MODEL ); BaseClass::Spawn(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFProjectile_EnergyBall::Precache() { PrecacheParticleSystem( "drg_cow_rockettrail_charged" ); PrecacheParticleSystem( "drg_cow_rockettrail_charged_blue" ); PrecacheParticleSystem( "drg_cow_rockettrail_normal" ); PrecacheParticleSystem( "drg_cow_rockettrail_normal_blue" ); PrecacheModel( ENERGY_BALL_MODEL ); PrecacheScriptSound( "Weapon_CowMangler.Explode" ); BaseClass::Precache(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFProjectile_EnergyBall::SetScorer( CBaseEntity *pScorer ) { m_Scorer = pScorer; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CBasePlayer *CTFProjectile_EnergyBall::GetScorer( void ) { return dynamic_cast( m_Scorer.Get() ); } //----------------------------------------------------------------------------- // Purpose: Plays an impact sound. Louder for the attacker. //----------------------------------------------------------------------------- void CTFProjectile_EnergyBall::ImpactSound( const char *pszSoundName, bool bLoudForAttacker ) { CTFPlayer *pAttacker = ToTFPlayer( GetScorer() ); if ( !pAttacker ) return; if ( bLoudForAttacker ) { float soundlen = 0; EmitSound_t params; params.m_flSoundTime = 0; params.m_pSoundName = pszSoundName; params.m_pflSoundDuration = &soundlen; CPASFilter filter( GetAbsOrigin() ); filter.RemoveRecipient( ToTFPlayer(pAttacker) ); EmitSound( filter, entindex(), params ); CSingleUserRecipientFilter attackerFilter( ToTFPlayer(pAttacker) ); EmitSound( attackerFilter, pAttacker->entindex(), params ); } else { EmitSound( pszSoundName ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFProjectile_EnergyBall::FadeOut( int iTime ) { SetMoveType( MOVETYPE_NONE ); SetAbsVelocity( vec3_origin ); AddSolidFlags( FSOLID_NOT_SOLID ); AddEffects( EF_NODRAW ); // Start remove timer. SetContextThink( &CTFProjectile_EnergyBall::RemoveThink, gpGlobals->curtime + iTime, "ENERGY_BALL_REMOVE_THINK" ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFProjectile_EnergyBall::RemoveThink( void ) { UTIL_Remove( this ); } //----------------------------------------------------------------------------- // Purpose: Arrow was deflected. //----------------------------------------------------------------------------- void CTFProjectile_EnergyBall::Deflected( CBaseEntity *pDeflectedBy, Vector &vecDir ) { CTFPlayer *pTFDeflector = ToTFPlayer( pDeflectedBy ); if ( !pTFDeflector ) return; ChangeTeam( pTFDeflector->GetTeamNumber() ); SetLauncher( pTFDeflector->GetActiveWeapon() ); CTFPlayer* pOldOwner = ToTFPlayer( GetOwnerEntity() ); SetOwnerEntity( pTFDeflector ); if ( pOldOwner ) { pOldOwner->SpeakConceptIfAllowed( MP_CONCEPT_DEFLECTED, "projectile:1,victim:1" ); } if ( pTFDeflector->m_Shared.IsCritBoosted() ) { SetCritical( true ); } CTFWeaponBase::SendObjectDeflectedEvent( pTFDeflector, pOldOwner, GetWeaponID(), this ); IncrementDeflected(); SetScorer( pTFDeflector ); // Change particle color data. if ( GetTeamNumber() == TF_TEAM_BLUE ) { m_vColor1 = TF_PARTICLE_WEAPON_BLUE_1; m_vColor2 = TF_PARTICLE_WEAPON_BLUE_2; } else { m_vColor1 = TF_PARTICLE_WEAPON_RED_1; m_vColor2 = TF_PARTICLE_WEAPON_RED_2; } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFProjectile_EnergyBall::Explode( trace_t *pTrace, CBaseEntity *pOther ) { if ( ShouldNotDetonate() ) { Destroy( true ); return; } // 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 ) ); } // Particle (oriented) Vector vecOrigin = GetAbsOrigin(); CPVSFilter filter( vecOrigin ); QAngle angExplosion( 0.f, 0.f, 0.f ); VectorAngles( pTrace->plane.normal, angExplosion ); TE_TFParticleEffect( filter, 0.f, GetExplosionParticleName(), vecOrigin, pTrace->plane.normal, angExplosion, NULL ); // Screenshake if ( m_bChargedShot ) { UTIL_ScreenShake( WorldSpaceCenter(), 25.0, 150.0, 1.0, 750, SHAKE_START ); } // Sound ImpactSound( "Weapon_CowMangler.Explode" ); CSoundEnt::InsertSound ( SOUND_COMBAT, vecOrigin, 1024, 3.0 ); // Damage. CBaseEntity *pAttacker = GetOwnerEntity(); IScorer *pScorerInterface = dynamic_cast( pAttacker ); if ( pScorerInterface ) { pAttacker = pScorerInterface->GetScorer(); } float flRadius = GetRadius(); if ( pAttacker ) // No attacker, deal no damage. Otherwise we could potentially kill teammates. { CTFPlayer *pTarget = ToTFPlayer( GetEnemy() ); if ( pTarget ) { // Rocket Specialist CheckForStunOnImpact( pTarget ); if ( pTarget->GetTeamNumber() != pAttacker->GetTeamNumber() ) { IGameEvent *event = gameeventmanager->CreateEvent( "projectile_direct_hit" ); if ( event ) { event->SetInt( "attacker", pAttacker->entindex() ); event->SetInt( "victim", pTarget->entindex() ); item_definition_index_t ownerWeaponDefIndex = INVALID_ITEM_DEF_INDEX; CTFWeaponBase *pWeapon = dynamic_cast< CTFWeaponBase * >( GetOriginalLauncher() ); if ( pWeapon ) { ownerWeaponDefIndex = pWeapon->GetAttributeContainer()->GetItem()->GetItemDefIndex(); } event->SetInt( "weapon_def_index", ownerWeaponDefIndex ); gameeventmanager->FireEvent( event, true ); } } } CTakeDamageInfo info( this, pAttacker, m_hLauncher, vec3_origin, vecOrigin, GetDamage(), GetDamageType(), GetDamageCustom() ); CTFRadiusDamageInfo radiusinfo( &info, vecOrigin, flRadius, NULL, m_bChargedShot ? TF_ROCKET_RADIUS_FOR_RJS*1.33 : TF_ROCKET_RADIUS_FOR_RJS ); TFGameRules()->RadiusDamage( radiusinfo ); } // Don't decal players with scorch. if ( !pOther->IsPlayer() ) { UTIL_DecalTrace( pTrace, "Scorch" ); } // Remove the rocket. UTIL_Remove( this ); return; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- const char *CTFProjectile_EnergyBall::GetExplosionParticleName( void ) { if ( m_bChargedShot ) { return ( GetTeamNumber() == TF_TEAM_RED ) ? "drg_cow_explosioncore_charged" : "drg_cow_explosioncore_charged_blue"; } else { return ( GetTeamNumber() == TF_TEAM_RED ) ? "drg_cow_explosioncore_normal" : "drg_cow_explosioncore_normal_blue"; } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- float CTFProjectile_EnergyBall::GetDamage() { return m_flDamage; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- int CTFProjectile_EnergyBall::GetDamageType() { int iDamageType; if ( m_bChargedShot ) { iDamageType = DMG_BLAST | DMG_HALF_FALLOFF | DMG_USEDISTANCEMOD | DMG_IGNITE; } else { iDamageType = DMG_BLAST | DMG_HALF_FALLOFF | DMG_USEDISTANCEMOD; } return iDamageType; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- int CTFProjectile_EnergyBall::GetDamageCustom() { return m_bChargedShot ? TF_DMG_CUSTOM_PLASMA_CHARGED : TF_DMG_CUSTOM_PLASMA; }