Advertisement
Guest User

Untitled

a guest
Sep 13th, 2012
149
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 17.49 KB | None | 0 0
  1. //========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:     357 - hand gun
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "NPCEvent.h"
  9. #include "basehlcombatweapon.h"
  10. #include "basecombatcharacter.h"
  11. #include "AI_BaseNPC.h"
  12. #include "player.h"
  13. #include "gamerules.h"
  14. #include "in_buttons.h"
  15. #include "soundent.h"
  16. #include "game.h"
  17. #include "vstdlib/random.h"
  18. #include "engine/IEngineSound.h"
  19. #include "te_effect_dispatch.h"
  20. #include "beam_flags.h"
  21. #include "ieffects.h"
  22. #include "sprite.h"
  23. #include "explode.h"
  24. #include "ammodef.h"
  25.  
  26. // memdbgon must be the last include file in a .cpp file!!!
  27. #include "tier0/memdbgon.h"
  28.  
  29. #ifndef CLIENT_DLL
  30. ConVar weapon_proto1_rate_of_fire( "weapon_proto1_rate_of_fire", "5" );
  31. ConVar weapon_proto1_mass_limit( "weapon_proto1_mass_limit", "250" );
  32. ConVar weapon_proto1_alt_fire_range( "weapon_proto1_alt_fire_range", "250" );
  33. ConVar weapon_proto1_ping_delay( "weapon_proto1_ping_delay", "1" );
  34. ConVar weapon_proto1_ping_radius( "weapon_proto1_ping_radius", "1200" );
  35. ConVar weapon_proto1_penetrate_depth( "weapon_proto1_penetrate_depth", "24" );
  36. ConVar weapon_proto1_exit_blast_radius( "weapon_proto1_exit_blast_radius", "100" );
  37. ConVar weapon_proto1_echo_sound_duration( "weapon_proto1_echo_sound_duration", "0.25" );
  38. #endif
  39.  
  40. #define PENETRATING_BOLT_MODEL "models/crossbow_bolt.mdl"
  41.  
  42. // The sound that is heard when the ping detects an object.
  43. #define SONAR_ECHO_SOUND    "Weapon_Proto1.Echo"
  44.  
  45. // The sound the player hears when activating a ping
  46. #define SONAR_PING_SOUND "Weapon_Proto1.Ping"
  47.  
  48. // The sound played by a physics object that is being consumed
  49. #define CONSUME_OBJECT_SOUND "Weapon_AR2.NPC_Double"
  50.  
  51. //-----------------------------------------------------------------------------
  52. // CWeaponProto1
  53. //-----------------------------------------------------------------------------
  54. class CWeaponProto1 : public CBaseHLCombatWeapon
  55. {
  56.     DECLARE_CLASS( CWeaponProto1, CBaseHLCombatWeapon );
  57. public:
  58.  
  59.     CWeaponProto1( void );
  60.  
  61.     void    Precache( void );
  62.     void    ItemPostFrame( void );
  63.     void    PrimaryAttack( void );
  64.     void    SecondaryAttack( void );
  65.     void    Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator );
  66.     void    Equip( CBaseCombatCharacter *pOwner );
  67.  
  68.     void    FireHitscanBolt();
  69.     bool    CanHitscanBoltReachTarget();
  70.  
  71.     void    FirePenetratingBolt();
  72.     bool    CanPenetratingBoltReachTarget( CBaseEntity *pTarget );
  73.     bool    CanConsumeObject( CBaseEntity *pObject );
  74.     void    ConsumeObject( CBaseEntity *pObject );
  75.     void    SendPing();
  76.     void    DoPingEffect();
  77.     void    DoPingSearch();
  78.     void    PingEntity( CBaseEntity *pEntity );
  79.  
  80.     CBaseEntity *DetectObject();
  81.  
  82.     float   m_flTimeNextPing;
  83.     int     m_iPingSpriteTexture;
  84.  
  85.     int     m_iEchoSoundsToDo;
  86.     float   m_flTimeNextEchoSound;
  87.     float   m_flEchoSoundDelay;
  88.  
  89.     DECLARE_SERVERCLASS();
  90.     DECLARE_DATADESC();
  91. };
  92.  
  93. LINK_ENTITY_TO_CLASS( weapon_proto1, CWeaponProto1 );
  94.  
  95. PRECACHE_WEAPON_REGISTER( weapon_proto1 );
  96.  
  97. IMPLEMENT_SERVERCLASS_ST( CWeaponProto1, DT_WeaponProto1 )
  98. END_SEND_TABLE()
  99.  
  100. BEGIN_DATADESC( CWeaponProto1 )
  101.     DEFINE_FIELD( m_flTimeNextPing, FIELD_TIME ),
  102.     DEFINE_FIELD( m_iEchoSoundsToDo, FIELD_INTEGER ),
  103.     DEFINE_FIELD( m_flTimeNextEchoSound, FIELD_TIME ),
  104.     DEFINE_FIELD( m_flEchoSoundDelay, FIELD_FLOAT ),
  105. END_DATADESC()
  106.  
  107. //-----------------------------------------------------------------------------
  108. // Purpose: Constructor
  109. //-----------------------------------------------------------------------------
  110. CWeaponProto1::CWeaponProto1( void )
  111. {
  112.     m_bReloadsSingly    = false;
  113.     m_bFiresUnderwater  = false;
  114.     m_iEchoSoundsToDo = 0;
  115. }
  116.  
  117. //-----------------------------------------------------------------------------
  118. // Purpose:
  119. //-----------------------------------------------------------------------------
  120. void CWeaponProto1::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator )
  121. {
  122.     CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  123.  
  124.     switch( pEvent->event )
  125.     {
  126.         case EVENT_WEAPON_RELOAD:
  127.             {
  128.                 CEffectData data;
  129.  
  130.                 // Emit six spent shells
  131.                 for ( int i = 0; i < 6; i++ )
  132.                 {
  133.                     data.m_vOrigin = pOwner->WorldSpaceCenter() + RandomVector( -4, 4 );
  134.                     data.m_vAngles = QAngle( 90, random->RandomInt( 0, 360 ), 0 );
  135.                     data.m_nEntIndex = entindex();
  136.  
  137.                     DispatchEffect( "ShellEject", data );
  138.                 }
  139.  
  140.                 break;
  141.             }
  142.     }
  143. }
  144.  
  145. //-----------------------------------------------------------------------------
  146. //-----------------------------------------------------------------------------
  147. void CWeaponProto1::FireHitscanBolt()
  148. {
  149.     if( !HasAnyAmmo() )
  150.     {
  151.         WeaponSound( EMPTY );
  152.         return;
  153.     }
  154.  
  155.     CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  156.  
  157.     if ( pOwner == NULL )
  158.         return;
  159.  
  160.     Vector vecAiming    = pOwner->GetAutoaimVector( 0 );
  161.     Vector vecSrc       = pOwner->Weapon_ShootPosition();
  162.  
  163.     QAngle angAiming;
  164.     VectorAngles( vecAiming, angAiming );
  165.  
  166.     // We're committed to the shot now. So take the ammo.
  167.     pOwner->RemoveAmmo( 1, m_iPrimaryAmmoType );
  168.  
  169.     trace_t tr;
  170.     // Trace the initial shot from the weapon
  171.     UTIL_TraceLine( vecSrc, vecSrc + vecAiming * MAX_COORD_INTEGER, MASK_SHOT, pOwner, COLLISION_GROUP_NONE, &tr );
  172.     NDebugOverlay::Line( vecSrc + Vector( 0, 0, -3 ), tr.endpos, 255, 255, 0, false, 0.1f );
  173.  
  174.     if( tr.m_pEnt == NULL )
  175.     {
  176.         // Bail out if we hit nothing at all.
  177.         return;
  178.     }
  179.  
  180.     // Record the entity that the beam struck.
  181.     CBaseEntity *pObjectStruck = tr.m_pEnt;
  182.    
  183.     if( pObjectStruck->MyNPCPointer() && pObjectStruck->IsAlive() )
  184.     {
  185.         CTakeDamageInfo damageInfo( this, pOwner, tr.m_pEnt->GetHealth(), DMG_GENERIC | DMG_DISSOLVE );
  186.         Vector force( 0, 0, 100.0f );
  187.         damageInfo.SetDamageForce( force );
  188.         damageInfo.SetDamagePosition( tr.endpos );
  189.         tr.m_pEnt->TakeDamage( damageInfo );
  190.     }
  191.  
  192.     // Mark the point where the shot entered an object.
  193.     //NDebugOverlay::Cross3D( tr.endpos, 8, 255, 255, 255, false, 60.0f );
  194.  
  195.     // Trace the second, punched-through shot. First, we have to locate the exit point.
  196.     // There are two methods for this. If the shot struck the world, we have to trace out of solid.
  197.     // If the shot struck an entity, we simply trace again, ignoring that entity.
  198.     CBaseEntity *pIgnoreEnt = NULL;
  199.  
  200.     if( pObjectStruck->IsWorld() )
  201.     {
  202.         pIgnoreEnt = pOwner;
  203.         // World method. Trace till you leave solid, then continue.
  204.        
  205.         // Move the trace "cursor" into the surface
  206.         Vector vecStart = tr.endpos + vecAiming * 1.0f;
  207.  
  208.         // Now see if this solid is thin enough to penetrate.
  209.         UTIL_TraceLine( vecStart, vecStart + vecAiming * weapon_proto1_penetrate_depth.GetFloat(), MASK_SHOT, pIgnoreEnt, COLLISION_GROUP_NONE, &tr );
  210.  
  211.         if( tr.fractionleftsolid <= 0.0f || tr.fractionleftsolid >= 1.0f )
  212.         {
  213.             // Wall can not be penetrated (too thick)
  214.             return;
  215.         }
  216.  
  217.         // This is a bit hacky, overwriting this value, but it lets us re-use code below.
  218.         tr.endpos = vecStart + vecAiming * (weapon_proto1_penetrate_depth.GetFloat() * tr.fractionleftsolid);
  219.     }
  220.     else
  221.     {
  222.         // Ignore whatever was struck.
  223.         pIgnoreEnt = tr.m_pEnt;
  224.     }
  225.  
  226.     //NDebugOverlay::Cross3D( tr.endpos, 8, 255, 255, 255, false, 60.0f );
  227.  
  228.     // Now just ignore the ent and trace again.
  229.     UTIL_TraceLine( tr.endpos, tr.endpos + vecAiming * MAX_COORD_INTEGER, MASK_SHOT, pIgnoreEnt, COLLISION_GROUP_NONE, &tr );
  230.  
  231.     if( tr.m_pEnt != NULL )
  232.     {
  233.         NDebugOverlay::Line( tr.startpos, tr.endpos, 255, 0, 255, false, 0.1f );
  234.  
  235.         if( tr.m_pEnt->MyNPCPointer() && tr.m_pEnt->IsAlive() )
  236.         {
  237.             Vector force( 0, 0, 1 );
  238.             CTakeDamageInfo damageInfo( this, pOwner, tr.m_pEnt->GetHealth(), DMG_GENERIC | DMG_DISSOLVE );
  239.             damageInfo.SetDamageForce( force );
  240.             damageInfo.SetDamagePosition( tr.endpos );
  241.             tr.m_pEnt->TakeDamage( damageInfo );
  242.         }
  243.     }
  244. }
  245.  
  246. //-----------------------------------------------------------------------------
  247. //-----------------------------------------------------------------------------
  248. void CWeaponProto1::Equip( CBaseCombatCharacter *pOwner )
  249. {
  250.     BaseClass::Equip( pOwner );
  251.  
  252.     pOwner->GiveAmmo( 3, "ammo_proto1" );
  253. }
  254.  
  255. //-----------------------------------------------------------------------------
  256. //-----------------------------------------------------------------------------
  257. void CWeaponProto1::Precache( void )
  258. {
  259.     BaseClass::Precache();
  260.     m_iPingSpriteTexture = PrecacheModel( "sprites/physbeam.vmt" );
  261.  
  262.     UTIL_PrecacheOther( "penetrating_bolt" );
  263.  
  264.     PrecacheSound( SONAR_ECHO_SOUND );
  265.     PrecacheSound( SONAR_PING_SOUND );
  266.     PrecacheSound( CONSUME_OBJECT_SOUND );
  267. }
  268.  
  269. //-----------------------------------------------------------------------------
  270. //-----------------------------------------------------------------------------
  271. void CWeaponProto1::ItemPostFrame( void )
  272. {
  273.     BaseClass::ItemPostFrame();
  274.  
  275.     CBaseEntity *pObject = DetectObject();
  276.  
  277.     if( pObject && CanConsumeObject( pObject ) )
  278.     {
  279.         g_pEffects->Sparks( pObject->WorldSpaceCenter() );
  280.     }
  281.  
  282.     if( m_iEchoSoundsToDo > 0 && gpGlobals->curtime >= m_flTimeNextEchoSound )
  283.     {
  284.         EmitSound( SONAR_ECHO_SOUND );
  285.         m_iEchoSoundsToDo--;
  286.  
  287.         m_flTimeNextEchoSound = gpGlobals->curtime + m_flEchoSoundDelay;
  288.     }
  289. }
  290.  
  291. //-----------------------------------------------------------------------------
  292. // Purpose:
  293. //-----------------------------------------------------------------------------
  294. void CWeaponProto1::PrimaryAttack( void )
  295. {
  296.     // Only the player fires this way so we can cast
  297.     CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
  298.  
  299.     if ( !pPlayer )
  300.     {
  301.         return;
  302.     }
  303.  
  304.     SendWeaponAnim( ACT_VM_PRIMARYATTACK );
  305.     pPlayer->SetAnimation( PLAYER_ATTACK1 );
  306.  
  307.     WeaponSound( SINGLE );
  308.     pPlayer->DoMuzzleFlash();
  309.  
  310.     //FirePenetratingBolt();
  311.     FireHitscanBolt();
  312.  
  313.     float flRateOfFireDelay = 1.0f / weapon_proto1_rate_of_fire.GetFloat();
  314.     m_flNextPrimaryAttack = gpGlobals->curtime + flRateOfFireDelay;
  315.  
  316.     pPlayer->ViewPunch( QAngle( -1, random->RandomFloat( -1, 1 ), 0 ) );
  317.  
  318.     if ( !HasAnyAmmo() )
  319.     {
  320.         // HEV suit - indicate out of ammo condition
  321.         pPlayer->SetSuitUpdate( "!HEV_AMO0", FALSE, 0 );
  322.     }
  323. }
  324.  
  325. //-----------------------------------------------------------------------------
  326. //-----------------------------------------------------------------------------
  327. void CWeaponProto1::SecondaryAttack( void )
  328. {
  329.     m_flNextSecondaryAttack = gpGlobals->curtime + 0.1f;
  330.  
  331.     // See if we can detect an object that can be eaten
  332.     CBaseEntity *pObject = DetectObject();
  333.     if( pObject )
  334.     {
  335.         if( CanConsumeObject( pObject ) )
  336.         {
  337.             ConsumeObject( pObject );
  338.         }
  339.  
  340.         return;
  341.     }
  342.  
  343.     // Otherwise, try to Ping
  344.     if( m_flTimeNextPing > gpGlobals->curtime )
  345.     {
  346.         // Too soon!
  347.         return;
  348.     }
  349.  
  350.     SendPing();
  351. }
  352.  
  353. //-----------------------------------------------------------------------------
  354. // Do a bunch of computations and determine whether a bolt fired from this location
  355. // could punch through a wall and harm this target. This function assumes that
  356. // the attacker does NOT have line of sight to the target.
  357. //
  358. // A bolt can reach the target IF:
  359. //  -The target is not protected by a solid that is thicker than
  360. //   the weapon's ability to penetrate solids
  361. //
  362. //  -Only one solid separates the attacker from the target.
  363. //-----------------------------------------------------------------------------
  364. bool CWeaponProto1::CanPenetratingBoltReachTarget( CBaseEntity *pTarget )
  365. {
  366.     trace_t tr;
  367.     CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
  368.  
  369.     if( !pPlayer )
  370.     {
  371.         return false;
  372.     }
  373.  
  374.     UTIL_TraceLine( pTarget->WorldSpaceCenter(), pPlayer->EyePosition(), MASK_SHOT, pTarget, COLLISION_GROUP_NONE, &tr );
  375.  
  376.     // This target is too far from the wall if his world space center is farther from the wall than the weapon's blast radius.
  377.     // (The explosion that accompanies the bolt's exit from the solid would not reach the target)
  378.     float flDistFromWall = (tr.startpos - tr.endpos).Length();
  379.  
  380.     if( flDistFromWall > weapon_proto1_exit_blast_radius.GetFloat() )
  381.         return false;
  382.    
  383.     // At this point in the function, we know that the target is close enough to the solid to be harmed by the explosion
  384.     // caused when the bolt exits the solid. Now we just need to check to see if the solid is thin enough for the bolt to
  385.     // pass through. To do this, we trace from the player to the target and measure the distance between the two traces' endpoints.
  386.     Vector vecTargetEndPos = tr.endpos;
  387.     UTIL_TraceLine( pPlayer->EyePosition(), pTarget->WorldSpaceCenter(), MASK_SHOT, pPlayer, COLLISION_GROUP_NONE, &tr );
  388.     float flSolidThickness = (tr.endpos - vecTargetEndPos).Length();
  389.  
  390.     if( flSolidThickness > weapon_proto1_penetrate_depth.GetFloat() )
  391.     {
  392.         // In this case, the endpoints of the target's traceline and the player's traceline are farther apart than the bolt's penetration
  393.         // depth. This either means that there is a single solid between the two that is too thick to penetrate, or that there are
  394.         // several solids between the two. Either is a failure case.
  395.         return false;
  396.     }
  397.  
  398.     return true;
  399. }
  400.  
  401. //-----------------------------------------------------------------------------
  402. //-----------------------------------------------------------------------------
  403. bool CWeaponProto1::CanConsumeObject( CBaseEntity *pObject )
  404. {
  405.     IPhysicsObject *pPhysObject = pObject->VPhysicsGetObject();
  406.  
  407.     if( !pPhysObject )
  408.     {
  409.         Msg("Object does not have VPhysics\n");
  410.         return false;
  411.     }
  412.  
  413.     if( pPhysObject->GetMass() > weapon_proto1_mass_limit.GetFloat() )
  414.     {
  415.         Msg("Object too heavy\n");
  416.         return false;
  417.     }
  418.  
  419.     if( !pObject->ClassMatches( "prop_physics" ) )
  420.     {
  421.         return false;
  422.     }
  423.  
  424.     return true;
  425. }
  426.  
  427. //-----------------------------------------------------------------------------
  428. //-----------------------------------------------------------------------------
  429. void CWeaponProto1::ConsumeObject( CBaseEntity *pObject )
  430. {
  431.     CBaseAnimating *pAnimating = dynamic_cast<CBaseAnimating*>(pObject);
  432.  
  433.     if( !pAnimating )
  434.     {
  435.         Msg("FAILED to consume %s\n", pObject->GetClassname() );
  436.         return;
  437.     }
  438.  
  439.     pAnimating->Dissolve( NULL, gpGlobals->curtime, false, ENTITY_DISSOLVE_CORE );
  440.     pAnimating->EmitSound( CONSUME_OBJECT_SOUND );
  441.        
  442.     Msg("GULP!\n");
  443.  
  444.     m_flNextSecondaryAttack = gpGlobals->curtime + 1.0f;
  445.  
  446.     GetOwner()->GiveAmmo( 1, "ammo_proto1" );
  447. }
  448.  
  449. //-----------------------------------------------------------------------------
  450. //-----------------------------------------------------------------------------
  451. void CWeaponProto1::SendPing()
  452. {
  453.     EmitSound( SONAR_PING_SOUND );
  454.  
  455.     DoPingEffect();
  456.     DoPingSearch();
  457.     m_flTimeNextPing = gpGlobals->curtime + weapon_proto1_ping_delay.GetFloat();
  458. }
  459.  
  460. //-----------------------------------------------------------------------------
  461. //-----------------------------------------------------------------------------
  462. void CWeaponProto1::DoPingEffect()
  463. {
  464.     CBroadcastRecipientFilter filter;
  465.  
  466.     te->BeamRingPoint( filter, 0, GetAbsOrigin(),   //origin
  467.         8.0f,   //start radius
  468.         weapon_proto1_ping_radius.GetFloat() * 2.0f,        //end radius
  469.         m_iPingSpriteTexture, //texture
  470.         0,          //halo index
  471.         0,          //start frame
  472.         2,          //framerate
  473.         0.5f,       //life
  474.         64,         //width
  475.         0,          //spread
  476.         0,          //amplitude
  477.         255,    //r
  478.         255,    //g
  479.         225,    //b
  480.         128,    //a
  481.         0,      //speed
  482.         FBEAM_FADEOUT
  483.         );
  484. }
  485.  
  486. //-----------------------------------------------------------------------------
  487. //-----------------------------------------------------------------------------
  488. void CWeaponProto1::DoPingSearch()
  489. {
  490.     CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
  491.  
  492.     Assert( pPlayer != NULL );
  493.  
  494.     CAI_BaseNPC **ppAIs = g_AI_Manager.AccessAIs();
  495.     int nAIs = g_AI_Manager.NumAIs();
  496.  
  497.     float flMaxDistSqr = Square( weapon_proto1_ping_radius.GetFloat() );
  498.     int iFoundTargets = 0;
  499.  
  500.     for ( int i = 0; i < nAIs; i++ )
  501.     {
  502.         CAI_BaseNPC *pNPC = ppAIs[ i ];
  503.         // Start rejecting.
  504.  
  505.         // Dead
  506.         if( !pNPC->IsAlive() )
  507.             continue;
  508.  
  509.         // Out of range
  510.         if( pNPC->GetAbsOrigin().DistToSqr( pPlayer->GetAbsOrigin() ) > flMaxDistSqr )
  511.             continue;
  512.  
  513.         PingEntity( pNPC );
  514.         iFoundTargets++;
  515.     }
  516.  
  517.     // Now we set up to make the echo sounds. Store off how many to make, (one per target discovered)
  518.     // when to do the next one, and the time between them. This makes sure
  519.     // that the weapon makes the correct number of echo sounds over the
  520.     // lifetime of the ping. It's not synchronized with the visual effect,
  521.     // but it still works well. (sjb)
  522.     m_iEchoSoundsToDo = iFoundTargets;
  523.     m_flTimeNextEchoSound = gpGlobals->curtime;
  524.     m_flEchoSoundDelay = weapon_proto1_echo_sound_duration.GetFloat() / ((float)iFoundTargets);
  525. }
  526.  
  527. //-----------------------------------------------------------------------------
  528. //-----------------------------------------------------------------------------
  529. void CWeaponProto1::PingEntity( CBaseEntity *pEntity )
  530. {
  531.     CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
  532.     CAI_BaseNPC *pNPC = pEntity->MyNPCPointer();
  533.  
  534.     if( !pPlayer || !pNPC )
  535.         return;
  536.  
  537.     bool bEnemy = pNPC->IRelationType( pPlayer ) != D_LI;
  538.  
  539.     if( bEnemy )
  540.     {
  541.         pNPC->StartPingEffect();
  542.     }
  543. }
  544.  
  545. //-----------------------------------------------------------------------------
  546. // See if the proto weapon is pointed at a physics object that can be eaten
  547. //-----------------------------------------------------------------------------
  548. CBaseEntity *CWeaponProto1::DetectObject()
  549. {
  550.     trace_t tr;
  551.  
  552.     CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
  553.     Vector vecSrc       = pPlayer->Weapon_ShootPosition();
  554.     Vector vecAiming    = pPlayer->GetAutoaimVector( AUTOAIM_SCALE_DEFAULT );
  555.  
  556.     UTIL_TraceLine( vecSrc, vecSrc + vecAiming * weapon_proto1_alt_fire_range.GetFloat(), (MASK_SHOT|CONTENTS_GRATE), pPlayer, COLLISION_GROUP_NONE, &tr );
  557.  
  558.     if( tr.m_pEnt && tr.m_pEnt->VPhysicsGetObject() != NULL )
  559.     {
  560.         return tr.m_pEnt;
  561.     }
  562.  
  563.     return NULL;
  564. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement