Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

402 lines
12 KiB

//========= 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<CTFProjectile_EnergyBall*>( 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<CBasePlayer *>( 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<IScorer*>( 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;
}