//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: Game-specific explosion effects // //=============================================================================// #include "cbase.h" #include "c_te_effect_dispatch.h" #include "tempent.h" #include "c_te_legacytempents.h" #include "tf_shareddefs.h" #include "engine/IEngineSound.h" #include "tf_weapon_parse.h" #include "c_basetempentity.h" #include "tier0/vprof.h" #include "c_tf_player.h" #include "econ_item_system.h" #include "c_tf_fx.h" #include "networkstringtabledefs.h" //-------------------------------------------------------------------------------------------------------------- CTFWeaponInfo *GetTFWeaponInfo( int iWeapon ) { // Get the weapon information. const char *pszWeaponAlias = WeaponIdToAlias( iWeapon ); if ( !pszWeaponAlias ) { return NULL; } WEAPON_FILE_INFO_HANDLE hWpnInfo = LookupWeaponInfoSlot( pszWeaponAlias ); if ( hWpnInfo == GetInvalidWeaponInfoHandle() ) { return NULL; } CTFWeaponInfo *pWeaponInfo = static_cast( GetFileWeaponInfoFromHandle( hWpnInfo ) ); return pWeaponInfo; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void TFExplosionCallback( const Vector &vecOrigin, const Vector &vecNormal, int iWeaponID, ClientEntityHandle_t hEntity, int nDefID, int nSound, int iCustomParticleIndex ) { // Get the weapon information. CTFWeaponInfo *pWeaponInfo = NULL; switch( iWeaponID ) { case TF_WEAPON_GRENADE_PIPEBOMB: case TF_WEAPON_GRENADE_DEMOMAN: case TF_WEAPON_PUMPKIN_BOMB: pWeaponInfo = GetTFWeaponInfo( TF_WEAPON_PIPEBOMBLAUNCHER ); break; case TF_WEAPON_FLAMETHROWER_ROCKET: pWeaponInfo = GetTFWeaponInfo( TF_WEAPON_FLAMETHROWER ); break; default: pWeaponInfo = GetTFWeaponInfo( iWeaponID ); break; } bool bIsPlayer = false; if ( hEntity.Get() ) { C_BaseEntity *pEntity = C_BaseEntity::Instance( hEntity ); if ( pEntity && pEntity->IsPlayer() ) { bIsPlayer = true; } } // Calculate the angles, given the normal. bool bIsWater = ( UTIL_PointContents( vecOrigin ) & CONTENTS_WATER ); bool bInAir = false; QAngle angExplosion( 0.0f, 0.0f, 0.0f ); // Cannot use zeros here because we are sending the normal at a smaller bit size. if ( fabs( vecNormal.x ) < 0.05f && fabs( vecNormal.y ) < 0.05f && fabs( vecNormal.z ) < 0.05f ) { bInAir = true; angExplosion.Init(); } else { VectorAngles( vecNormal, angExplosion ); bInAir = false; } // Base explosion effect and sound. const char *pszEffect = "ExplosionCore_wall"; const char *pszSound = "BaseExplosionEffect.Sound"; // check for a custom particle effect if ( iCustomParticleIndex != INVALID_STRING_INDEX ) { pszEffect = GetParticleSystemNameFromIndex( iCustomParticleIndex ); } if ( pWeaponInfo ) { // Explosions. if ( iCustomParticleIndex == INVALID_STRING_INDEX ) { if ( bIsWater ) { if ( Q_strlen( pWeaponInfo->m_szExplosionWaterEffect ) > 0 ) { pszEffect = pWeaponInfo->m_szExplosionWaterEffect; } } else { if ( bIsPlayer || bInAir ) { if ( Q_strlen( pWeaponInfo->m_szExplosionPlayerEffect ) > 0 ) { pszEffect = pWeaponInfo->m_szExplosionPlayerEffect; } } else { if ( Q_strlen( pWeaponInfo->m_szExplosionEffect ) > 0 ) { pszEffect = pWeaponInfo->m_szExplosionEffect; } } } } // Sound. if ( Q_strlen( pWeaponInfo->m_szExplosionSound ) > 0 ) { // Check for a replacement sound in the itemdef first - defaults to SPECIAL1 if ( nDefID >= 0 ) { C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer(); if ( pLocalPlayer ) { CEconItemDefinition *pItemDef = ItemSystem()->GetStaticDataForItemByDefIndex( nDefID ); if ( pItemDef ) { pszSound = (char *)pItemDef->GetWeaponReplacementSound( pLocalPlayer->GetTeamNumber(), (WeaponSound_t)nSound ); if ( !pszSound || !pszSound[0] ) { pszSound = pWeaponInfo->m_szExplosionSound; } } } } else { pszSound = pWeaponInfo->m_szExplosionSound; } } } if ( iWeaponID == TF_WEAPON_PUMPKIN_BOMB ) { pszSound = "Halloween.PumpkinExplode"; } CLocalPlayerFilter filter; C_BaseEntity::EmitSound( filter, SOUND_FROM_WORLD, pszSound, &vecOrigin ); if ( GameRules() ) { pszEffect = GameRules()->TranslateEffectForVisionFilter( "particles", pszEffect ); } DispatchParticleEffect( pszEffect, vecOrigin, angExplosion ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- class C_TETFExplosion : public C_BaseTempEntity { public: DECLARE_CLASS( C_TETFExplosion, C_BaseTempEntity ); DECLARE_CLIENTCLASS(); C_TETFExplosion( void ); virtual void PostDataUpdate( DataUpdateType_t updateType ); public: Vector m_vecOrigin; Vector m_vecNormal; int m_iWeaponID; ClientEntityHandle_t m_hEntity; int m_nDefID; int m_nSound; int m_iCustomParticleIndex; }; //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- C_TETFExplosion::C_TETFExplosion( void ) { m_vecOrigin.Init(); m_vecNormal.Init(); m_iWeaponID = TF_WEAPON_NONE; m_hEntity = INVALID_EHANDLE_INDEX; m_nDefID = -1; m_nSound = SPECIAL1; m_iCustomParticleIndex = INVALID_STRING_INDEX; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void C_TETFExplosion::PostDataUpdate( DataUpdateType_t updateType ) { VPROF( "C_TETFExplosion::PostDataUpdate" ); TFExplosionCallback( m_vecOrigin, m_vecNormal, m_iWeaponID, m_hEntity, m_nDefID, m_nSound, m_iCustomParticleIndex ); } static void RecvProxy_ExplosionEntIndex( const CRecvProxyData *pData, void *pStruct, void *pOut ) { int nEntIndex = pData->m_Value.m_Int; // The 'new' encoding for INVALID_EHANDLE_INDEX is 2047, but the old encoding // was -1. Old demos and replays will use the old encoding so we have to check // for it. The field is now unsigned so -1 will not be created in new replays. ((C_TETFExplosion*)pStruct)->m_hEntity = (nEntIndex == kInvalidEHandleExplosion || nEntIndex == -1) ? INVALID_EHANDLE_INDEX : ClientEntityList().EntIndexToHandle( nEntIndex ); } IMPLEMENT_CLIENTCLASS_EVENT_DT( C_TETFExplosion, DT_TETFExplosion, CTETFExplosion ) RecvPropFloat( RECVINFO( m_vecOrigin[0] ) ), RecvPropFloat( RECVINFO( m_vecOrigin[1] ) ), RecvPropFloat( RECVINFO( m_vecOrigin[2] ) ), RecvPropVector( RECVINFO( m_vecNormal ) ), RecvPropInt( RECVINFO( m_iWeaponID ) ), RecvPropInt( "entindex", 0, SIZEOF_IGNORE, 0, RecvProxy_ExplosionEntIndex ), RecvPropInt( RECVINFO( m_nDefID ) ), RecvPropInt( RECVINFO( m_nSound ) ), RecvPropInt( RECVINFO( m_iCustomParticleIndex ) ), END_RECV_TABLE()