summaryrefslogtreecommitdiff
path: root/game/shared/tf/tf_weapon_bat.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/shared/tf/tf_weapon_bat.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'game/shared/tf/tf_weapon_bat.cpp')
-rw-r--r--game/shared/tf/tf_weapon_bat.cpp1294
1 files changed, 1294 insertions, 0 deletions
diff --git a/game/shared/tf/tf_weapon_bat.cpp b/game/shared/tf/tf_weapon_bat.cpp
new file mode 100644
index 0000000..8413426
--- /dev/null
+++ b/game/shared/tf/tf_weapon_bat.cpp
@@ -0,0 +1,1294 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "cbase.h"
+#include "tf_weapon_bat.h"
+#include "decals.h"
+
+// Client specific.
+#ifdef CLIENT_DLL
+#include "c_basedoor.h"
+#include "c_tf_player.h"
+#include "IEffects.h"
+#include "bone_setup.h"
+#include "c_tf_gamestats.h"
+// Server specific.
+#else
+#include "doors.h"
+#include "tf_player.h"
+#include "tf_ammo_pack.h"
+#include "tf_gamestats.h"
+#include "ilagcompensationmanager.h"
+#include "collisionutils.h"
+#include "particle_parse.h"
+#include "tf_projectile_base.h"
+#include "tf_gamerules.h"
+#endif
+
+const float DEFAULT_ORNAMENT_EXPLODE_RADIUS = 50.0f;
+const float DEFAULT_ORNAMENT_EXPLODE_DAMAGE_MULT = 0.9f;
+
+//=============================================================================
+//
+// Weapon Bat tables.
+//
+
+// TFBat --
+IMPLEMENT_NETWORKCLASS_ALIASED( TFBat, DT_TFWeaponBat )
+
+BEGIN_NETWORK_TABLE( CTFBat, DT_TFWeaponBat )
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CTFBat )
+END_PREDICTION_DATA()
+
+LINK_ENTITY_TO_CLASS( tf_weapon_bat, CTFBat );
+PRECACHE_WEAPON_REGISTER( tf_weapon_bat );
+// -- TFBat
+
+
+// TFBat_Fish --
+IMPLEMENT_NETWORKCLASS_ALIASED( TFBat_Fish, DT_TFWeaponBat_Fish )
+
+BEGIN_NETWORK_TABLE( CTFBat_Fish, DT_TFWeaponBat_Fish )
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CTFBat_Fish )
+END_PREDICTION_DATA()
+
+LINK_ENTITY_TO_CLASS( tf_weapon_bat_fish, CTFBat_Fish );
+PRECACHE_WEAPON_REGISTER( tf_weapon_bat_fish );
+// -- TFBat_Fish
+
+
+// TFBat_Wood --
+IMPLEMENT_NETWORKCLASS_ALIASED( TFBat_Wood, DT_TFWeaponBat_Wood )
+
+BEGIN_NETWORK_TABLE( CTFBat_Wood, DT_TFWeaponBat_Wood )
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CTFBat_Wood )
+END_PREDICTION_DATA()
+
+LINK_ENTITY_TO_CLASS( tf_weapon_bat_wood, CTFBat_Wood );
+PRECACHE_WEAPON_REGISTER( tf_weapon_bat_wood );
+// -- TFBat_Wood
+
+
+// CTFBat_Giftwrap --
+IMPLEMENT_NETWORKCLASS_ALIASED( TFBat_Giftwrap, DT_TFWeaponBat_Giftwrap )
+
+BEGIN_NETWORK_TABLE( CTFBat_Giftwrap, DT_TFWeaponBat_Giftwrap )
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CTFBat_Giftwrap )
+END_PREDICTION_DATA()
+
+LINK_ENTITY_TO_CLASS( tf_weapon_bat_giftwrap, CTFBat_Giftwrap );
+PRECACHE_WEAPON_REGISTER( tf_weapon_bat_giftwrap );
+// -- CTFBat_Giftwrap
+
+
+// TFStunBall --
+IMPLEMENT_NETWORKCLASS_ALIASED( TFStunBall, DT_TFProjectile_StunBall )
+BEGIN_NETWORK_TABLE( CTFStunBall, DT_TFProjectile_StunBall )
+END_NETWORK_TABLE()
+
+LINK_ENTITY_TO_CLASS( tf_projectile_stun_ball, CTFStunBall );
+PRECACHE_WEAPON_REGISTER( tf_projectile_stun_ball );
+
+#define TF_WEAPON_STUNBALL_VM_MODEL "models/weapons/v_models/v_baseball.mdl"
+#define TF_WEAPON_STUNBALL_MODEL "models/weapons/w_models/w_baseball.mdl"
+
+#if defined( GAME_DLL )
+ConVar tf_scout_stunball_base_duration( "tf_scout_stunball_base_duration", "6.0", FCVAR_DEVELOPMENTONLY );
+ConVar tf_scout_stunball_base_speed( "tf_scout_stunball_base_speed", "3000", FCVAR_DEVELOPMENTONLY );
+ConVar sv_proj_stunball_damage( "sv_proj_stunball_damage", "15", FCVAR_DEVELOPMENTONLY );
+#endif
+// -- TFStunBall
+
+
+// CTFBall_Ornament --
+IMPLEMENT_NETWORKCLASS_ALIASED( TFBall_Ornament, DT_TFProjectileBall_Ornament )
+BEGIN_NETWORK_TABLE( CTFBall_Ornament, DT_TFProjectileBall_Ornament )
+END_NETWORK_TABLE()
+
+LINK_ENTITY_TO_CLASS( tf_projectile_ball_ornament, CTFBall_Ornament );
+PRECACHE_WEAPON_REGISTER( tf_projectile_ball_ornament );
+
+#define TF_WEAPON_BALL_ORNAMENT_VM_MODEL "models/weapons/c_models/c_xms_festive_ornament.mdl"
+#define TF_WEAPON_BALL_ORNAMENT_MODEL "models/weapons/c_models/c_xms_festive_ornament.mdl"
+
+#if defined( GAME_DLL )
+//ConVar tf_scout_stunball_base_duration( "tf_scout_stunball_base_duration", "6.0", FCVAR_DEVELOPMENTONLY );
+#endif
+// -- CTFBall_Ornament
+
+
+
+static string_t s_iszTrainName;
+
+//=============================================================================
+#define STUNBALL_TRAIL_ALPHA 128
+
+
+//=============================================================================
+//
+// CTFBat
+//
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CTFBat::CTFBat()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFBat::Smack( void )
+{
+ BaseClass::Smack();
+
+#ifdef GAME_DLL
+ if ( BatDeflects() )
+ {
+#ifdef TF_RAID_MODE
+ if ( TFGameRules()->IsRaidMode() )
+ {
+ }
+ else
+#endif // TF_RAID_MODE
+ {
+ DeflectProjectiles();
+ }
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFBat::PlayDeflectionSound( bool bPlayer )
+{
+ WeaponSound( MELEE_HIT_WORLD );
+}
+
+//=============================================================================
+//
+// CTFBat_Wood
+//
+
+CTFBat_Wood::CTFBat_Wood()
+{
+ m_iEnemyBallID = 0;
+#ifdef CLIENT_DLL
+ m_hStunBallVM = NULL;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+ConVar tf_scout_bat_launch_delay( "tf_scout_bat_launch_delay", "0.1", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY );
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFBat_Wood::LaunchBallThink( void )
+{
+ CTFPlayer *pPlayer = GetTFPlayerOwner();
+ if ( !pPlayer )
+ return;
+
+ LaunchBall();
+
+#ifdef GAME_DLL
+ pPlayer->SpeakWeaponFire( MP_CONCEPT_BAT_BALL );
+ CTF_GameStats.Event_PlayerFiredWeapon( pPlayer, IsCurrentAttackACrit() );
+#endif
+#ifdef CLIENT_DLL
+ C_CTF_GameStats.Event_PlayerFiredWeapon( pPlayer, IsCurrentAttackACrit() );
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFBat_Wood::SecondaryAttackAnim( CTFPlayer *pPlayer )
+{
+ pPlayer->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_SECONDARY );
+}
+
+// SERVER ONLY --
+#ifdef GAME_DLL
+
+//-----------------------------------------------------------------------------
+// Purpose: Calculate the ball's initial position, angle, and velocity.
+//-----------------------------------------------------------------------------
+void CTFBat_Wood::GetBallDynamics( Vector& vecLoc, QAngle& vecAngles, Vector& vecVelocity, AngularImpulse& angImpulse, CTFPlayer* pPlayer )
+{
+ Vector vecForward, vecUp;
+ AngleVectors( pPlayer->EyeAngles(), &vecForward, NULL, &vecUp );
+ vecLoc = pPlayer->GetAbsOrigin() + pPlayer->GetModelScale() * ( Vector( 0, 0, 50 ) + vecForward * 32.f );
+ vecAngles = pPlayer->GetAbsAngles();
+
+ // Calculate the initial impulse on the item.
+ vecVelocity = Vector( 0.0f, 0.0f, 0.0f );
+ vecVelocity += vecForward * 10;
+ vecVelocity += vecUp * 1;
+ VectorNormalize( vecVelocity );
+ vecVelocity *= tf_scout_stunball_base_speed.GetInt();
+
+ angImpulse = AngularImpulse( 0, random->RandomFloat( 0, 100 ), 0 );
+}
+
+// -- SERVER ONLY
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFBat_Wood::SecondaryAttack( void )
+{
+ CTFPlayer *pPlayer = GetTFPlayerOwner();
+ if ( !pPlayer )
+ return;
+
+ if ( !CanAttack() )
+ return;
+
+ if ( m_flNextPrimaryAttack > gpGlobals->curtime )
+ return;
+
+ // Do we have any balls? If so, use them.
+ int iBallCount = pPlayer->GetAmmoCount( TF_AMMO_GRENADES1 );
+ if ( (iBallCount > 0) && CanCreateBall( pPlayer ) )
+ {
+ SecondaryAttackAnim( pPlayer );
+ SendWeaponAnim( ACT_VM_PRIMARYATTACK );
+
+ SetContextThink( &CTFBat_Wood::LaunchBallThink, gpGlobals->curtime + tf_scout_bat_launch_delay.GetFloat(), "LAUNCH_BALL_THINK" );
+
+ m_flNextPrimaryAttack = gpGlobals->curtime + 0.25;
+
+#ifdef GAME_DLL
+ if ( pPlayer->m_Shared.IsStealthed() )
+ {
+ pPlayer->RemoveInvisibility();
+ }
+#endif // GAME_DLL
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Client Only. Show the stunball view model if necessary.
+//-----------------------------------------------------------------------------
+#ifdef CLIENT_DLL
+void CTFBat_Wood::SetWeaponVisible( bool visible )
+{
+ BaseClass::SetWeaponVisible( visible );
+
+ if ( !m_hStunBallVM )
+ return;
+
+ if ( visible )
+ {
+ m_hStunBallVM->RemoveEffects( EF_NODRAW );
+ }
+ else
+ {
+ m_hStunBallVM->AddEffects( EF_NODRAW );
+ }
+}
+#endif
+
+#ifdef CLIENT_DLL
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFBat_Wood::OnDataChanged( DataUpdateType_t updateType )
+{
+ BaseClass::OnDataChanged( updateType );
+
+ bool bLocalPlayerAmmo = true;
+ if ( GetPlayerOwner() == C_BasePlayer::GetLocalPlayer() )
+ {
+ bLocalPlayerAmmo = GetPlayerOwner()->GetAmmoCount( TF_AMMO_GRENADES1 ) > 0;
+ }
+
+ if ( IsCarrierAlive() && ( WeaponState() == WEAPON_IS_ACTIVE ) && bLocalPlayerAmmo == true )
+ {
+ AddBallChild();
+ }
+ else
+ {
+ RemoveBallChild();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Client Only. Show the stunball view model if necessary.
+//-----------------------------------------------------------------------------
+void CTFBat_Wood::AddBallChild( void )
+{
+ CTFPlayer *pPlayer = GetTFPlayerOwner();
+ if ( !pPlayer )
+ return;
+
+ if ( !pPlayer->IsLocalPlayer() )
+ return;
+
+ if ( !pPlayer->GetViewModel() )
+ return;
+
+ if ( m_hStunBallVM )
+ return;
+
+ CTFViewModel* pBall = new class CTFViewModel();
+ if ( pBall != NULL )
+ {
+ pBall->InitializeAsClientEntity( GetBallViewModelName(), RENDER_GROUP_OPAQUE_ENTITY );
+ pBall->SetAbsOrigin( pPlayer->GetViewModel()->GetAbsOrigin() );
+ pBall->SetModel( GetBallViewModelName() );
+ pBall->m_nSkin = ( pPlayer->GetTeamNumber() == TF_TEAM_BLUE ) ? 1 : 0;
+
+ CStudioHdr *pStudioHdr = pPlayer->GetViewModel()->GetModelPtr();
+ if ( pStudioHdr )
+ {
+ int iAttachment = Studio_FindAttachment( pStudioHdr, "weapon_bone_L" ) + 1;
+ pBall->SetParent( pPlayer->GetViewModel(), iAttachment );
+ }
+ pBall->AddEffects( EF_BONEMERGE );
+ pBall->SetMoveType( MOVETYPE_NONE );
+ pBall->AddSolidFlags( FSOLID_NOT_SOLID );
+ m_hStunBallVM.Set( pBall );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFBat_Wood::Drop( const Vector &vecVelocity )
+{
+ BaseClass::Drop( vecVelocity );
+
+ RemoveBallChild();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFBat_Wood::WeaponReset( void )
+{
+ RemoveBallChild();
+
+ BaseClass::WeaponReset();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFBat_Wood::UpdateOnRemove( void )
+{
+ RemoveBallChild();
+
+ BaseClass::UpdateOnRemove();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFBat_Wood::RemoveBallChild()
+{
+ if ( m_hStunBallVM )
+ {
+ m_hStunBallVM->Remove();
+ m_hStunBallVM = NULL;
+ }
+}
+
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose: Determines if there is space to create a ball.
+//-----------------------------------------------------------------------------
+bool CTFBat_Wood::CanCreateBall( CTFPlayer* pPlayer )
+{
+ int iWeaponMod = 0;
+ CALL_ATTRIB_HOOK_INT( iWeaponMod, set_weapon_mode );
+ if ( iWeaponMod == 0 )
+ return false;
+
+ if ( pPlayer->GetWaterLevel() == WL_Eyes )
+ return false;
+
+ Vector vecForward, vecUp;
+ AngleVectors( pPlayer->EyeAngles(), &vecForward, NULL, &vecUp );
+ Vector vecBallStart = pPlayer->GetAbsOrigin() + Vector( 0, 0, 50 );
+ Vector vecBallEnd = vecBallStart + vecForward * 32.f;
+
+ // Trace out and see if we hit a wall.
+ trace_t trace;
+ CTraceFilterSimple traceFilter( this, COLLISION_GROUP_NONE );
+ UTIL_TraceHull( vecBallStart, vecBallEnd, -Vector(8,8,8), Vector(8,8,8), MASK_SOLID_BRUSHONLY, &traceFilter, &trace );
+ if ( trace.DidHitWorld() || trace.startsolid )
+ return false;
+ else
+ {
+ if ( trace.m_pEnt )
+ {
+ // Don't let the player bat through doors.
+ CBaseDoor *pDoor = dynamic_cast<CBaseDoor*>( trace.m_pEnt );
+ if ( pDoor )
+ return false;
+ }
+ return true;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFBat_Wood::LaunchBall( void )
+{
+ CTFPlayer *pPlayer = GetTFPlayerOwner();
+ if ( !pPlayer )
+ return;
+
+#if GAME_DLL
+ // Make a ball.
+ CBaseEntity* pBall = CreateBall();
+ if ( !pBall )
+ return;
+
+ if ( IsCurrentAttackACrit() )
+ {
+ WeaponSound( BURST );
+ }
+ WeaponSound( SPECIAL2 );
+ pPlayer->RemoveAmmo( 1, TF_AMMO_GRENADES1 );
+#endif
+
+ StartEffectBarRegen();
+}
+
+// SERVER ONLY --
+#ifdef GAME_DLL
+
+//-----------------------------------------------------------------------------
+// Purpose: The wooden bat creates a baseball that stuns whomever it hits.
+//-----------------------------------------------------------------------------
+CBaseEntity* CTFBat_Wood::CreateBall( void )
+{
+ CTFPlayer *pPlayer = GetTFPlayerOwner();
+ if ( !pPlayer )
+ return NULL;
+
+ // Do another check here, as the player may have moved to an invalid position
+ // since the first check (0.1 seconds ago). This fixes the ball sometimes
+ // going through thin geometry, such as windows and spawn blockers.
+ if ( !CanCreateBall( pPlayer ) )
+ return NULL;
+
+ // Determine the ball's initial location, angles, and velocity.
+ Vector vecLocation, vecVelocity;
+ QAngle vecAngles;
+ AngularImpulse angImpulse;
+ GetBallDynamics( vecLocation, vecAngles, vecVelocity, angImpulse, pPlayer );
+
+ // Create a stun ball.
+ CTFStunBall* pBall = CTFStunBall::Create( vecLocation, vecAngles, pPlayer );
+ Assert( pBall );
+ if ( !pBall )
+ return NULL;
+
+ CalcIsAttackCritical();
+
+ pBall->m_iOriginalOwnerID = m_iEnemyBallID;
+ m_iEnemyBallID = 0;
+
+ pBall->SetCritical( IsCurrentAttackACrit() );
+ pBall->InitGrenade( vecVelocity, angImpulse, pPlayer, GetTFWpnData() );
+ pBall->SetLauncher( this );
+ pBall->SetOwnerEntity( pPlayer );
+ pBall->SetInitialSpeed( tf_scout_stunball_base_speed.GetInt() );
+
+ return pBall;
+}
+
+// -- SERVER ONLY
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose: Play pickup anim when we grab a new ball.
+//-----------------------------------------------------------------------------
+void CTFBat_Wood::PickedUpBall( void )
+{
+ if ( WeaponState() == WEAPON_IS_ACTIVE )
+ {
+ SendWeaponAnim( ACT_VM_PULLBACK_SPECIAL );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Play animation appropriate to ball status.
+//-----------------------------------------------------------------------------
+bool CTFBat_Wood::SendWeaponAnim( int iActivity )
+{
+ CTFPlayer *pPlayer = GetTFPlayerOwner();
+ if ( !pPlayer )
+ return BaseClass::SendWeaponAnim( iActivity );
+
+ if ( pPlayer->GetAmmoCount( TF_AMMO_GRENADES1 ) > 0 )
+ {
+ switch ( iActivity )
+ {
+ case ACT_VM_DRAW:
+ iActivity = ACT_VM_DRAW_SPECIAL;
+ break;
+ case ACT_VM_HOLSTER:
+ iActivity = ACT_VM_HOLSTER_SPECIAL;
+ break;
+ case ACT_VM_IDLE:
+ iActivity = ACT_VM_IDLE_SPECIAL;
+ break;
+ case ACT_VM_PULLBACK:
+ iActivity = ACT_VM_PULLBACK_SPECIAL;
+ break;
+ case ACT_VM_PRIMARYATTACK:
+ iActivity = ACT_VM_PRIMARYATTACK_SPECIAL;
+ break;
+ case ACT_VM_SECONDARYATTACK:
+ iActivity = ACT_VM_PRIMARYATTACK_SPECIAL;
+ break;
+ case ACT_VM_HITCENTER:
+ iActivity = ACT_VM_HITCENTER_SPECIAL;
+ break;
+ case ACT_VM_SWINGHARD:
+ iActivity = ACT_VM_SWINGHARD_SPECIAL;
+ break;
+ case ACT_VM_IDLE_TO_LOWERED:
+ iActivity = ACT_VM_IDLE_TO_LOWERED_SPECIAL;
+ break;
+ case ACT_VM_IDLE_LOWERED:
+ iActivity = ACT_VM_IDLE_LOWERED_SPECIAL;
+ break;
+ case ACT_VM_LOWERED_TO_IDLE:
+ iActivity = ACT_VM_LOWERED_TO_IDLE_SPECIAL;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return BaseClass::SendWeaponAnim( iActivity );
+}
+
+//=============================================================================
+//
+// CTFStunBall
+//
+
+// SERVER ONLY --
+#ifdef GAME_DLL
+
+CTFStunBall::CTFStunBall()
+{
+ s_iszTrainName = AllocPooledString( "models/props_vehicles/train_enginecar.mdl" );
+ m_iOriginalOwnerID = 0;
+ m_pBallTrail = NULL;
+ m_flBallTrailLife = 1.0f;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Static entity factory.
+//-----------------------------------------------------------------------------
+CTFStunBall* CTFStunBall::Create( const Vector &vecOrigin, const QAngle &vecAngles, CBaseEntity *pOwner )
+{
+ CTFStunBall* pBall = static_cast<CTFStunBall*>( CBaseAnimating::CreateNoSpawn( "tf_projectile_stun_ball", vecOrigin, vecAngles, pOwner ) );
+ if ( pBall )
+ {
+ DispatchSpawn( pBall );
+ }
+
+ return pBall;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFStunBall::Precache( void )
+{
+ PrecacheModel( GetBallModelName() );
+ PrecacheModel( GetBallViewModelName() );
+ PrecacheModel( "effects/baseballtrail_red.vmt" );
+ PrecacheModel( "effects/baseballtrail_blu.vmt" );
+
+ BaseClass::Precache();
+}
+
+
+//-----------------------------------------------------------------------------
+const char *CTFStunBall::GetBallModelName( void ) const
+{
+ return TF_WEAPON_STUNBALL_MODEL;
+}
+
+
+//-----------------------------------------------------------------------------
+const char *CTFStunBall::GetBallViewModelName( void ) const
+{
+ return TF_WEAPON_STUNBALL_VM_MODEL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets up initial properties.
+//-----------------------------------------------------------------------------
+void CTFStunBall::Spawn( void )
+{
+ BaseClass::Spawn();
+
+ SetModel( GetBallModelName() );
+ VPhysicsDestroyObject();
+ VPhysicsInitNormal( SOLID_BBOX, 0, false );
+
+ AddSolidFlags( FSOLID_TRIGGER );
+ AddFlag( FL_GRENADE );
+
+ SetCollisionGroup( COLLISION_GROUP_PROJECTILE );
+ m_takedamage = DAMAGE_NO;
+
+ SetContextThink( &CBaseEntity::SUB_Remove, gpGlobals->curtime + 15, "DieContext" );
+
+ // Draw the trail for the Baseball on spawn
+ if ( !m_pBallTrail )
+ {
+ const char *pTrailTeamName = ( GetTeamNumber() == TF_TEAM_RED ) ? "effects/baseballtrail_red.vmt" : "effects/baseballtrail_blu.vmt";
+ CSpriteTrail *pTempTrail = NULL;
+
+ pTempTrail = CSpriteTrail::SpriteTrailCreate( pTrailTeamName, GetAbsOrigin(), true );
+ pTempTrail->FollowEntity( this );
+ pTempTrail->SetTransparency( kRenderTransAlpha, 255, 255, 255, STUNBALL_TRAIL_ALPHA, kRenderFxNone );
+ pTempTrail->SetStartWidth( 9 );
+ pTempTrail->SetTextureResolution( 1.0f / ( 96.0f * 1.0f ) );
+ pTempTrail->SetLifeTime( 0.4 );
+ pTempTrail->TurnOn();
+ pTempTrail->SetAttachment( this, 0 );
+ m_pBallTrail = pTempTrail;
+ SetContextThink( &CTFStunBall::RemoveBallTrail, gpGlobals->curtime + 3, "FadeBallTrail");
+ }
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFStunBall::Explode( trace_t *pTrace, int bitsDamageType )
+{
+ if ( !IsAllowedToExplode() )
+ return;
+
+ BaseClass::Explode( pTrace, bitsDamageType );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Stun the person we smashed into.
+//-----------------------------------------------------------------------------
+#define FLIGHT_TIME_TO_MAX_STUN 1.f
+void CTFStunBall::ApplyBallImpactEffectOnVictim( CBaseEntity *pOther )
+{
+ if ( !pOther || !pOther->IsPlayer() )
+ return;
+
+ CTFPlayer* pPlayer = ToTFPlayer( pOther );
+ if ( !pPlayer )
+ return;
+
+ CTFPlayer *pOwner = ToTFPlayer( GetOwnerEntity() );
+ if ( !pOwner )
+ return;
+
+ if ( m_bTouched )
+ return;
+
+ // Can't stun an invul player.
+ if ( pPlayer->m_Shared.IsInvulnerable() || pPlayer->m_Shared.InCond( TF_COND_INVULNERABLE_WEARINGOFF ) )
+ return;
+
+ // We have a more intense stun based on our travel time.
+ float flLifeTime = MIN( gpGlobals->curtime - m_flCreationTime, FLIGHT_TIME_TO_MAX_STUN );
+ float flLifeTimeRatio = flLifeTime / FLIGHT_TIME_TO_MAX_STUN;
+ if ( flLifeTimeRatio > 0.1f )
+ {
+
+ float flStun = 0.5f;
+ float flStunDuration = tf_scout_stunball_base_duration.GetFloat() * flLifeTimeRatio;
+ if ( IsCritical() )
+ flStunDuration += 2.0; // Extra two seconds of effect time if we're a critical hit.
+ int iStunFlags = TF_STUN_LOSER_STATE | TF_STUN_MOVEMENT;
+ if ( flLifeTimeRatio >= 1.f )
+ {
+ flStunDuration += 1.0;
+ iStunFlags = TF_STUN_CONTROLS;
+ iStunFlags |= TF_STUN_SPECIAL_SOUND;
+ CTF_GameStats.Event_PlayerStunBall( pOwner, true );
+ }
+ else
+ {
+ CTF_GameStats.Event_PlayerStunBall( pOwner, false );
+ }
+
+ // Adjust stun amount and flags if we're hitting a boss or scaled enemy
+ if ( TFGameRules() && TFGameRules()->GameModeUsesMiniBosses() && ( pPlayer->IsMiniBoss() || pPlayer->GetModelScale() > 1.0f ) )
+ {
+ // If max range, freeze them in place - otherwise adjust it based on distance
+ flStun = flLifeTimeRatio >= 1.f ? 1.f : RemapValClamped( flLifeTimeRatio, 0.1f, 0.99f, 0.5f, 0.75 );
+ iStunFlags = flLifeTimeRatio >= 1.f ? ( TF_STUN_SPECIAL_SOUND | TF_STUN_MOVEMENT ) : TF_STUN_MOVEMENT;
+ }
+
+ if ( pPlayer->GetWaterLevel() != WL_Eyes )
+ {
+ pPlayer->m_Shared.StunPlayer( flStunDuration, flStun, iStunFlags, pOwner );
+ if ( pPlayer->GetUserID() == m_iOriginalOwnerID )
+ {
+ // Holy crap! We just stunned a scout with their own ball.
+ // Give the player an achievement for this.
+ if ( pOwner->IsPlayerClass( TF_CLASS_SCOUT ) )
+ {
+ pOwner->AwardAchievement( ACHIEVEMENT_TF_SCOUT_STUN_SCOUT_WITH_THEIR_BALL );
+ }
+ }
+ }
+ }
+
+ // Give 'em a love tap.
+ const trace_t *pTrace = &CBaseEntity::GetTouchTrace();
+ trace_t *pNewTrace = const_cast<trace_t*>( pTrace );
+
+ CBaseEntity *pInflictor = GetLauncher();
+ CTakeDamageInfo info;
+ info.SetAttacker( GetOwnerEntity() );
+ info.SetInflictor( pInflictor );
+ info.SetWeapon( pInflictor );
+ info.SetDamage( GetDamage() );
+ info.SetDamageCustom( TF_DMG_CUSTOM_BASEBALL );
+ info.SetDamageForce( GetDamageForce() );
+ info.SetDamagePosition( GetAbsOrigin() );
+ int iDamageType = GetDamageType();
+ if ( IsCritical() )
+ iDamageType |= DMG_CRITICAL;
+ info.SetDamageType( iDamageType );
+
+ // Hurt 'em.
+ Vector dir;
+ AngleVectors( GetAbsAngles(), &dir );
+ pPlayer->DispatchTraceAttack( info, dir, pNewTrace );
+ ApplyMultiDamage();
+
+ // Make this ball fade faster now that it's hit something.
+ SetContextThink( &CBaseEntity::SUB_Remove, gpGlobals->curtime + 4, "DieContext" );
+
+ m_bTouched = true;
+}
+
+float CTFStunBall::GetDamage( void )
+{
+ return sv_proj_stunball_damage.GetFloat();
+}
+
+Vector CTFStunBall::GetDamageForce( void )
+{
+ Vector vecVelocity = GetAbsVelocity();
+ IPhysicsObject *pPhysicsObject = VPhysicsGetObject();
+ if ( pPhysicsObject )
+ {
+ pPhysicsObject->GetVelocity( &vecVelocity, NULL );
+ VectorNormalize( vecVelocity );
+ }
+
+ return (vecVelocity * GetDamage());
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: We hit something.
+//-----------------------------------------------------------------------------
+void CTFStunBall::PipebombTouch( CBaseEntity *pOther )
+{
+ CTFPlayer* pOwner = ToTFPlayer( GetOwnerEntity() );
+ if ( !pOwner )
+ return;
+
+ if ( !pOther || !pOther->IsSolid() || pOther->IsSolidFlagSet( FSOLID_VOLUME_CONTENTS ) )
+ {
+ pOwner->SpeakConceptIfAllowed( MP_CONCEPT_BALL_MISSED );
+ return;
+ }
+
+ // Go away if we're hit by a moving train.
+ if ( pOther->GetModelName() == s_iszTrainName && ( pOther->GetAbsVelocity().LengthSqr() > 1.0f ) )
+ {
+ UTIL_Remove( this );
+ return;
+ }
+
+ // Go away if we hit the skybox.
+ trace_t pTrace;
+ Vector velDir = GetAbsVelocity();
+ VectorNormalize( velDir );
+ Vector vecSpot = GetAbsOrigin() - velDir * 32;
+ UTIL_TraceLine( vecSpot, vecSpot + velDir * 64, MASK_SOLID, this, COLLISION_GROUP_NONE, &pTrace );
+ if ( pTrace.fraction < 1.0 && pTrace.surface.flags & SURF_SKY )
+ {
+ UTIL_Remove( this );
+ return;
+ }
+
+ // Ignore things that aren't players.
+ if ( !pOther->IsPlayer() )
+ return;
+
+ // If we hit a scout, pickup as ammo
+ if ( m_bTouched )
+ {
+ CTFPlayer* pPlayer = ToTFPlayer( pOther );
+ if ( pPlayer && pPlayer->IsPlayerClass( TF_CLASS_SCOUT ) &&
+ (pPlayer->GetAmmoCount( TF_AMMO_GRENADES1 ) < pPlayer->GetMaxAmmo( TF_AMMO_GRENADES1 )) )
+ {
+ pPlayer->GiveAmmo( 1, TF_AMMO_GRENADES1 );
+ RemoveBallTrail();
+ UTIL_Remove( this );
+
+ CTFBat_Wood *pBat = (CTFBat_Wood *) pPlayer->Weapon_OwnsThisID( TF_WEAPON_BAT_WOOD );
+ if ( pBat )
+ {
+ // If this ball came from an enemy scout, remember who they were...
+ if ( pPlayer->GetTeamNumber() != GetTeamNumber() )
+ {
+ if ( pOwner )
+ {
+ pBat->m_iEnemyBallID = pOwner->GetUserID();
+ }
+ }
+
+ // If we have the bat up, we need to play the correct anim.
+ pBat->PickedUpBall();
+ }
+
+ // Say something.
+ pPlayer->SpeakConceptIfAllowed( MP_CONCEPT_GRAB_BALL, (pOther->GetTeamNumber() == GetTeamNumber()) ? "my_team:1" : "my_team:0" );
+ }
+ return;
+ }
+
+ if ( pOther == GetThrower() )
+ return;
+
+ if ( !InSameTeam( pOther ) && pOther->m_takedamage != DAMAGE_NO )
+ {
+ ApplyBallImpactEffectOnVictim( pOther );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: We hit something.
+//-----------------------------------------------------------------------------
+void CTFStunBall::VPhysicsCollision( int index, gamevcollisionevent_t *pEvent )
+{
+ CTFPlayer* pOwner = ToTFPlayer( GetOwnerEntity() );
+ bool bWasTouched = m_bTouched;
+ BaseClass::VPhysicsCollision( index, pEvent );
+ if ( pOwner && !bWasTouched && m_bTouched )
+ {
+ pOwner->SpeakConceptIfAllowed( MP_CONCEPT_BALL_MISSED );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Fade and kill the trail
+//-----------------------------------------------------------------------------
+void CTFStunBall::RemoveBallTrail( void )
+{
+ if (!m_pBallTrail)
+ return;
+
+ if (m_pBallTrail)
+ {
+ if (m_flBallTrailLife <= 0)
+ {
+ UTIL_Remove( m_pBallTrail);
+ m_flBallTrailLife = 1.0f;
+ }
+ else
+ {
+ float fAlpha = STUNBALL_TRAIL_ALPHA * m_flBallTrailLife;
+
+ CSpriteTrail *pTempTrail = dynamic_cast< CSpriteTrail*>( m_pBallTrail.Get() );
+
+ if ( pTempTrail )
+ {
+ pTempTrail->SetBrightness( int(fAlpha) );
+ }
+
+ m_flBallTrailLife = m_flBallTrailLife - 0.1f;
+ SetContextThink( &CTFStunBall::RemoveBallTrail, gpGlobals->curtime + 0.05, "FadeBallTrail");
+ }
+ }
+}
+
+// -- SERVER ONLY
+#endif
+
+#ifdef CLIENT_DLL
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const char *CTFStunBall::GetTrailParticleName( void )
+{
+ int iTeamNumber = GetTeamNumber();
+
+ if ( GetDeflected() )
+ {
+ CTFPlayer *pOwner = ToTFPlayer( GetDeflectOwner() );
+
+ if ( pOwner )
+ {
+ iTeamNumber = pOwner->GetTeamNumber();
+ }
+ }
+ if ( iTeamNumber == TF_TEAM_BLUE )
+ {
+ return "stunballtrail_blue";
+ }
+ else
+ {
+ return "stunballtrail_red";
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFStunBall::CreateTrailParticles( void )
+{
+ if ( pEffectTrail )
+ {
+ ParticleProp()->StopEmission( pEffectTrail );
+ }
+ if ( pEffectCrit )
+ {
+ ParticleProp()->StopEmission( pEffectCrit );
+ }
+ pEffectTrail = ParticleProp()->Create( GetTrailParticleName(), PATTACH_ABSORIGIN_FOLLOW );
+ int iTeamNumber = GetTeamNumber();
+
+ if ( GetDeflected() )
+ {
+ CTFPlayer *pOwner = ToTFPlayer( GetDeflectOwner() );
+
+ if ( pOwner )
+ {
+ iTeamNumber = pOwner->GetTeamNumber();
+ }
+ }
+ if ( m_bCritical )
+ {
+ if ( iTeamNumber == TF_TEAM_BLUE )
+ {
+ pEffectCrit = ParticleProp()->Create( "stunballtrail_blue_crit", PATTACH_ABSORIGIN_FOLLOW );
+
+ }
+ else
+ {
+ pEffectCrit = ParticleProp()->Create( "stunballtrail_red_crit", PATTACH_ABSORIGIN_FOLLOW );
+ }
+ }
+}
+#endif
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFBat_Giftwrap::Spawn( void )
+{
+ BaseClass::Spawn();
+
+ m_nSkin = ( GetTeamNumber() == TF_TEAM_BLUE ) ? 1 : 0;
+}
+
+
+#ifdef GAME_DLL
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CBaseEntity *CTFBat_Giftwrap::CreateBall( void )
+{
+ CTFPlayer *pPlayer = GetTFPlayerOwner();
+ if ( !pPlayer )
+ return NULL;
+
+ // Do another check here, as the player may have moved to an invalid position
+ // since the first check (0.1 seconds ago). This fixes the ball sometimes
+ // going through thin geometry, such as windows and spawn blockers.
+ if ( !CanCreateBall( pPlayer ) )
+ return NULL;
+
+ // Determine the ball's initial location, angles, and velocity.
+ Vector vecLocation, vecVelocity;
+ QAngle vecAngles;
+ AngularImpulse angImpulse;
+ GetBallDynamics( vecLocation, vecAngles, vecVelocity, angImpulse, pPlayer );
+
+ // Create the ornament ball.
+ CTFBall_Ornament *pBall = CTFBall_Ornament::Create( vecLocation, vecAngles, pPlayer );
+ Assert( pBall );
+ if ( !pBall )
+ return NULL;
+
+ CalcIsAttackCritical();
+
+ pBall->m_iOriginalOwnerID = m_iEnemyBallID;
+ m_iEnemyBallID = 0;
+
+ pBall->SetCritical( IsCurrentAttackACrit() );
+ pBall->InitGrenade( vecVelocity, angImpulse, pPlayer, GetTFWpnData() );
+ pBall->SetLauncher( this );
+ pBall->SetOwnerEntity( pPlayer );
+ pBall->SetInitialSpeed( tf_scout_stunball_base_speed.GetInt() );
+ pBall->m_nSkin = ( pPlayer->GetTeamNumber() == TF_TEAM_BLUE ) ? 1 : 0;
+
+ return pBall;
+}
+
+
+//-----------------------------------------------------------------------------
+void CTFBall_Ornament::Precache( void )
+{
+ PrecacheScriptSound( "BallBuster.OrnamentImpactRange" );
+ PrecacheScriptSound( "BallBuster.OrnamentImpact" );
+ PrecacheScriptSound( "BallBuster.HitBall" );
+ PrecacheScriptSound( "BallBuster.HitFlesh" );
+ PrecacheScriptSound( "BallBuster.HitWorld" );
+ PrecacheScriptSound( "BallBuster.DrawCatch" );
+ PrecacheScriptSound( "BallBuster.Ornament_DrawCatch" );
+ PrecacheScriptSound( "BallBuster.Ball_HitWorld" );
+
+ BaseClass::Precache();
+}
+
+
+//-----------------------------------------------------------------------------
+CTFBall_Ornament *CTFBall_Ornament::Create( const Vector &vecOrigin, const QAngle &vecAngles, CBaseEntity *pOwner )
+{
+ CTFBall_Ornament* pBall = static_cast< CTFBall_Ornament * >( CBaseAnimating::CreateNoSpawn( "tf_projectile_ball_ornament", vecOrigin, vecAngles, pOwner ) );
+ if ( pBall )
+ {
+ DispatchSpawn( pBall );
+ }
+
+ return pBall;
+}
+
+
+//-----------------------------------------------------------------------------
+const char *CTFBall_Ornament::GetBallModelName( void ) const
+{
+ return TF_WEAPON_BALL_ORNAMENT_MODEL;
+}
+
+
+//-----------------------------------------------------------------------------
+const char *CTFBall_Ornament::GetBallViewModelName( void ) const
+{
+ return TF_WEAPON_BALL_ORNAMENT_VM_MODEL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFBall_Ornament::ApplyBallImpactEffectOnVictim( CBaseEntity *pOther )
+{
+ if ( !pOther || !pOther->IsPlayer() )
+ return;
+
+ CTFPlayer* pPlayer = ToTFPlayer( pOther );
+ if ( !pPlayer )
+ return;
+
+ CTFPlayer *pOwner = ToTFPlayer( GetOwnerEntity() );
+ if ( !pOwner )
+ return;
+
+ if ( m_bTouched )
+ return;
+
+ // Can't bleed an invul player.
+ if ( pPlayer->m_Shared.IsInvulnerable() || pPlayer->m_Shared.InCond( TF_COND_INVULNERABLE_WEARINGOFF ) )
+ return;
+
+ bool bIsCriticalHit = IsCritical();
+ float flBleedTime = 5.0f;
+ bool bIsLongRangeHit = false;
+
+ // long distance hit is always a crit
+ float flLifeTime = gpGlobals->curtime - m_flCreationTime;
+ if ( flLifeTime >= FLIGHT_TIME_TO_MAX_STUN )
+ {
+ bIsCriticalHit = true;
+ bIsLongRangeHit = true;
+ }
+
+ // just do the bleed effect directly since the bleed
+ // attribute comes from the inflictor, which is the bat.
+ pPlayer->m_Shared.MakeBleed( pOwner, (CTFBat_Giftwrap *)GetLauncher(), flBleedTime );
+
+ // Apply particle effect to victim (the remaining effects happen inside Explode)
+ DispatchParticleEffect( "xms_ornament_glitter", PATTACH_POINT_FOLLOW, pPlayer, "head" );
+
+ // Give 'em a love tap.
+ const trace_t *pTrace = &CBaseEntity::GetTouchTrace();
+ trace_t *pNewTrace = const_cast<trace_t*>( pTrace );
+
+ CBaseEntity *pInflictor = GetLauncher();
+ CTakeDamageInfo info;
+ info.SetAttacker( GetOwnerEntity() );
+ info.SetInflictor( pInflictor );
+ info.SetWeapon( pInflictor );
+ info.SetDamage( GetDamage() );
+ info.SetDamageCustom( TF_DMG_CUSTOM_BASEBALL );
+ info.SetDamageForce( GetDamageForce() );
+ info.SetDamagePosition( GetAbsOrigin() );
+ int iDamageType = GetDamageType();
+ if ( bIsCriticalHit )
+ iDamageType |= DMG_CRITICAL;
+ info.SetDamageType( iDamageType );
+
+ // Hurt 'em.
+ Vector dir;
+ AngleVectors( GetAbsAngles(), &dir );
+ pPlayer->DispatchTraceAttack( info, dir, pNewTrace );
+ ApplyMultiDamage();
+
+ // the ball shatters
+ UTIL_Remove( this );
+
+ m_bTouched = true;
+}
+
+void CTFBall_Ornament::PipebombTouch( CBaseEntity *pOther )
+{
+ CTFPlayer* pOwner = ToTFPlayer( GetOwnerEntity() );
+ if ( !pOwner )
+ return;
+
+ // Go away if we're hit by a moving train.
+ if ( pOther->GetModelName() == s_iszTrainName && ( pOther->GetAbsVelocity().LengthSqr() > 1.0f ) )
+ {
+ UTIL_Remove( this );
+ return;
+ }
+
+ // Go away if we hit the skybox.
+ trace_t pTrace;
+ Vector velDir = GetAbsVelocity();
+ VectorNormalize( velDir );
+ Vector vecSpot = GetAbsOrigin() - velDir * 32;
+ UTIL_TraceLine( vecSpot, vecSpot + velDir * 64, MASK_SOLID, this, COLLISION_GROUP_NONE, &pTrace );
+ if ( pTrace.fraction < 1.0 && pTrace.surface.flags & SURF_SKY )
+ {
+ UTIL_Remove( this );
+ return;
+ }
+
+ if ( pOther == GetThrower() )
+ return;
+
+ // Explode (does radius damage, triggers particles and sound effects).
+ Explode( &pTrace, DMG_BLAST|DMG_PREVENT_PHYSICS_FORCE );
+
+ if ( !InSameTeam( pOther ) && pOther->m_takedamage != DAMAGE_NO )
+ {
+ ApplyBallImpactEffectOnVictim( pOther );
+ }
+}
+
+void CTFBall_Ornament::VPhysicsCollision( int index, gamevcollisionevent_t *pEvent )
+{
+ BaseClass::VPhysicsCollision( index, pEvent );
+
+ int otherIndex = !index;
+ CBaseEntity *pHitEntity = pEvent->pEntities[otherIndex];
+
+ if ( !pHitEntity )
+ return;
+
+ // Break if we hit the world.
+ if ( pHitEntity->IsWorld() )
+ {
+ // Explode immediately next frame. (Can't explode in the collision callback.)
+ m_vCollisionVelocity = pEvent->preVelocity[index];
+ SetContextThink( &CTFBall_Ornament::VPhysicsCollisionThink, gpGlobals->curtime, "OrnamentCollisionThink" );
+ }
+}
+
+void CTFBall_Ornament::VPhysicsCollisionThink( void )
+{
+ trace_t pTrace;
+ Vector velDir = m_vCollisionVelocity;
+ VectorNormalize( velDir );
+ Vector vecSpot = GetAbsOrigin() - velDir * 16;
+ UTIL_TraceLine( vecSpot, vecSpot + velDir * 32, MASK_SOLID, this, COLLISION_GROUP_NONE, &pTrace );
+
+ Explode( &pTrace, DMG_BLAST|DMG_PREVENT_PHYSICS_FORCE );
+}
+
+void CTFBall_Ornament::Explode( trace_t *pTrace, int bitsDamageType )
+{
+ // Create smashed glass particles when we explode
+ CTFPlayer* pOwner = ToTFPlayer( GetOwnerEntity() );
+ if ( pOwner && pOwner->GetTeamNumber() == TF_TEAM_RED )
+ {
+ DispatchParticleEffect( "xms_ornament_smash_red", GetAbsOrigin(), GetAbsAngles() );
+ }
+ else
+ {
+ DispatchParticleEffect( "xms_ornament_smash_blue", GetAbsOrigin(), GetAbsAngles() );
+ }
+
+ Vector vecOrigin = GetAbsOrigin();
+
+ // sound effects
+ EmitSound_t params;
+ params.m_flSoundTime = 0;
+ params.m_pflSoundDuration = 0;
+ params.m_pSoundName = "BallBuster.OrnamentImpact";
+ CPASFilter filter( vecOrigin );
+ filter.RemoveRecipient( pOwner );
+ EmitSound( filter, entindex(), params );
+ CSingleUserRecipientFilter attackerFilter( pOwner );
+ EmitSound( attackerFilter, pOwner->entindex(), params );
+
+ // Explosion damage is some fraction of our base damage
+ float flExplodeDamage = GetDamage() * DEFAULT_ORNAMENT_EXPLODE_DAMAGE_MULT;
+
+ // Do radius damage
+ Vector vecBlastForce(0.0f, 0.0f, 0.0f);
+ CTakeDamageInfo info( this, GetThrower(), m_hLauncher, vecBlastForce, GetAbsOrigin(), flExplodeDamage, bitsDamageType, TF_DMG_CUSTOM_BASEBALL, &vecOrigin );
+ CTFRadiusDamageInfo radiusinfo( &info, vecOrigin, DEFAULT_ORNAMENT_EXPLODE_RADIUS, nullptr, 0.0f, 0.0f );
+ TFGameRules()->RadiusDamage( radiusinfo );
+
+ UTIL_Remove( this );
+}
+
+
+
+#endif
+