Counter Strike : Global Offensive Source Code
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.

722 lines
22 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "ai_basenpc.h"
  8. #include "animation.h"
  9. #include "basecombatweapon.h"
  10. #include "player.h" // For gEvilImpulse101 / CBasePlayer
  11. #include "gamerules.h" // For g_pGameRules
  12. #include <keyvalues.h>
  13. #include "ammodef.h"
  14. #include "baseviewmodel.h"
  15. #include "in_buttons.h"
  16. #include "soundent.h"
  17. #include "weapon_parse.h"
  18. #include "game.h"
  19. #include "engine/IEngineSound.h"
  20. #include "sendproxy.h"
  21. #include "tier1/strtools.h"
  22. #include "vphysics/constraints.h"
  23. #include "npcevent.h"
  24. #include "igamesystem.h"
  25. #include "collisionutils.h"
  26. #include "iservervehicle.h"
  27. #include "func_break.h"
  28. #if defined(PORTAL2)
  29. #include "weapon_portalgun.h"
  30. #endif
  31. // memdbgon must be the last include file in a .cpp file!!!
  32. #include "tier0/memdbgon.h"
  33. extern int gEvilImpulse101; // In Player.h
  34. // -----------------------------------------
  35. // Sprite Index info
  36. // -----------------------------------------
  37. int g_sModelIndexLaser; // holds the index for the laser beam
  38. const char *g_pModelNameLaser = "sprites/laserbeam.vmt";
  39. int g_sModelIndexLaserDot; // holds the index for the laser beam dot
  40. int g_sModelIndexFireball; // holds the index for the fireball
  41. int g_sModelIndexSmoke; // holds the index for the smoke cloud
  42. int g_sModelIndexWExplosion; // holds the index for the underwater explosion
  43. int g_sModelIndexBubbles; // holds the index for the bubbles model
  44. int g_sModelIndexBloodDrop; // holds the sprite index for the initial blood
  45. int g_sModelIndexBloodSpray; // holds the sprite index for splattered blood
  46. ConVar weapon_showproficiency( "weapon_showproficiency", "0" );
  47. extern ConVar ai_debug_shoot_positions;
  48. //-----------------------------------------------------------------------------
  49. // Purpose: Precache global weapon resources
  50. //-----------------------------------------------------------------------------
  51. PRECACHE_REGISTER_BEGIN( GLOBAL, WeaponResources )
  52. #if !defined( TF_DLL ) && !defined ( DOTA_DLL ) && !defined ( PORTAL2 ) && !defined ( CSTRIKE15 )
  53. PRECACHE_INDEX( MODEL, "sprites/zerogxplode.vmt", g_sModelIndexFireball )
  54. PRECACHE_INDEX( MODEL, "sprites/steam1.vmt", g_sModelIndexSmoke )
  55. PRECACHE_INDEX( MODEL, "sprites/bubble.vmt", g_sModelIndexBubbles )
  56. PRECACHE_INDEX( MODEL, "sprites/laserbeam.vmt", g_sModelIndexLaser )
  57. PRECACHE( PARTICLE_SYSTEM, "blood_impact_red_01" )
  58. PRECACHE( PARTICLE_SYSTEM, "blood_impact_green_01" )
  59. PRECACHE( PARTICLE_SYSTEM, "blood_impact_yellow_01" )
  60. PRECACHE( MODEL, "models/weapons/w_bullet.mdl" )
  61. PRECACHE( MODEL, "effects/bubble.vmt" )
  62. #endif // !TF_DLL
  63. #if !defined ( DOTA_DLL ) && !defined ( PORTAL2 )
  64. PRECACHE( GAMESOUND, "BaseCombatWeapon.WeaponDrop" )
  65. PRECACHE( GAMESOUND, "BaseCombatWeapon.WeaponMaterialize" )
  66. #endif
  67. PRECACHE_REGISTER_END()
  68. void W_Precache(void)
  69. {
  70. PrecacheFileWeaponInfoDatabase();
  71. }
  72. //-----------------------------------------------------------------------------
  73. // Purpose: Transmit weapon data
  74. //-----------------------------------------------------------------------------
  75. int CBaseCombatWeapon::UpdateTransmitState( void)
  76. {
  77. // If the weapon is being carried by a CBaseCombatCharacter, let the combat character do the logic
  78. // about whether or not to transmit it.
  79. if ( GetOwner() )
  80. {
  81. return SetTransmitState( FL_EDICT_PVSCHECK );
  82. }
  83. else
  84. {
  85. // If it's just lying around, then use CBaseEntity's visibility test to see if it should be sent.
  86. return BaseClass::UpdateTransmitState();
  87. }
  88. }
  89. void CBaseCombatWeapon::Operator_FrameUpdate( CBaseCombatCharacter *pOperator )
  90. {
  91. StudioFrameAdvance( ); // animate
  92. if ( IsSequenceFinished() )
  93. {
  94. if ( SequenceLoops() )
  95. {
  96. // animation does loop, which means we're playing subtle idle. Might need to fidget.
  97. int iSequence = SelectWeightedSequence( GetActivity() );
  98. if ( iSequence != ACTIVITY_NOT_AVAILABLE )
  99. {
  100. ResetSequence( iSequence ); // Set to new anim (if it's there)
  101. }
  102. }
  103. #if 0
  104. else
  105. {
  106. // animation that just ended doesn't loop! That means we just finished a fidget
  107. // and should return to our heaviest weighted idle (the subtle one)
  108. SelectHeaviestSequence( GetActivity() );
  109. }
  110. #endif
  111. }
  112. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  113. if ( pOwner == NULL )
  114. return;
  115. CBaseViewModel *vm = pOwner->GetViewModel( m_nViewModelIndex );
  116. if ( vm == NULL )
  117. return;
  118. // HACK: Player weapon and view model often use the same mdl, which results
  119. // in duplicate anim events. For now, let the view model handle the events
  120. // if they're the same, which is the preferred behavior in general.
  121. CStudioHdr *w_hdr = GetModelPtr();
  122. CStudioHdr *v_hdr = vm->GetModelPtr();
  123. if ( w_hdr->GetRenderHdr() != v_hdr->GetRenderHdr() )
  124. {
  125. // Animation events are passed back to the weapon's owner/operator
  126. DispatchAnimEvents( pOperator );
  127. }
  128. // Update and dispatch the viewmodel events
  129. if ( vm != NULL )
  130. {
  131. vm->StudioFrameAdvance();
  132. vm->DispatchAnimEvents( this );
  133. }
  134. }
  135. //-----------------------------------------------------------------------------
  136. // Purpose:
  137. // Input : *pEvent -
  138. // *pOperator -
  139. //-----------------------------------------------------------------------------
  140. void CBaseCombatWeapon::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator )
  141. {
  142. int nEvent = pEvent->Event();
  143. if ( (pEvent->type & AE_TYPE_NEWEVENTSYSTEM) && (pEvent->type & AE_TYPE_SERVER) )
  144. {
  145. if ( nEvent == AE_NPC_WEAPON_FIRE )
  146. {
  147. bool bSecondary = (atoi( pEvent->options ) != 0);
  148. Operator_ForceNPCFire( pOperator, bSecondary );
  149. }
  150. else if ( nEvent == AE_WPN_PLAYWPNSOUND )
  151. {
  152. int iSnd = GetWeaponSoundFromString(pEvent->options);
  153. if ( iSnd != -1 )
  154. {
  155. WeaponSound( (WeaponSound_t)iSnd );
  156. }
  157. }
  158. }
  159. }
  160. // NOTE: This should never be called when a character is operating the weapon. Animation events should be
  161. // routed through the character, and then back into CharacterAnimEvent()
  162. void CBaseCombatWeapon::HandleAnimEvent( animevent_t *pEvent )
  163. {
  164. //If the player is receiving this message, pass it through
  165. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  166. if ( pOwner != NULL )
  167. {
  168. Operator_HandleAnimEvent( pEvent, pOwner );
  169. }
  170. }
  171. //-----------------------------------------------------------------------------
  172. // Purpose: Make the weapon visible and tangible
  173. //-----------------------------------------------------------------------------
  174. CBaseEntity* CBaseCombatWeapon::Respawn( void )
  175. {
  176. // make a copy of this weapon that is invisible and inaccessible to players (no touch function). The weapon spawn/respawn code
  177. // will decide when to make the weapon visible and touchable.
  178. CBaseEntity *pNewWeapon = CBaseEntity::Create( GetClassname(), g_pGameRules->VecWeaponRespawnSpot( this ), GetLocalAngles(), GetOwnerEntity() );
  179. if ( pNewWeapon )
  180. {
  181. pNewWeapon->AddEffects( EF_NODRAW );// invisible for now
  182. pNewWeapon->SetTouch( NULL );// no touch
  183. pNewWeapon->SetThink( &CBaseCombatWeapon::AttemptToMaterialize );
  184. UTIL_DropToFloor( this, MASK_SOLID );
  185. // not a typo! We want to know when the weapon the player just picked up should respawn! This new entity we created is the replacement,
  186. // but when it should respawn is based on conditions belonging to the weapon that was taken.
  187. pNewWeapon->SetNextThink( gpGlobals->curtime + g_pGameRules->FlWeaponRespawnTime( this ) );
  188. }
  189. else
  190. {
  191. Warning("Respawn failed to create %s!\n", GetClassname() );
  192. }
  193. return pNewWeapon;
  194. }
  195. //-----------------------------------------------------------------------------
  196. // Purpose: Weapons ignore other weapons when LOS tracing
  197. //-----------------------------------------------------------------------------
  198. class CWeaponLOSFilter : public CTraceFilterSkipTwoEntities
  199. {
  200. DECLARE_CLASS( CWeaponLOSFilter, CTraceFilterSkipTwoEntities );
  201. public:
  202. CWeaponLOSFilter( IHandleEntity *pHandleEntity, IHandleEntity *pHandleEntity2, int collisionGroup ) :
  203. CTraceFilterSkipTwoEntities( pHandleEntity, pHandleEntity2, collisionGroup ), m_pVehicle( NULL )
  204. {
  205. // If the tracing entity is in a vehicle, then ignore it
  206. if ( pHandleEntity != NULL )
  207. {
  208. CBaseCombatCharacter *pBCC = ((CBaseEntity *)pHandleEntity)->MyCombatCharacterPointer();
  209. if ( pBCC != NULL )
  210. {
  211. m_pVehicle = pBCC->GetVehicleEntity();
  212. }
  213. }
  214. }
  215. virtual bool ShouldHitEntity( IHandleEntity *pServerEntity, int contentsMask )
  216. {
  217. CBaseEntity *pEntity = (CBaseEntity *)pServerEntity;
  218. if ( pEntity->GetCollisionGroup() == COLLISION_GROUP_WEAPON )
  219. return false;
  220. // Don't collide with the tracing entity's vehicle (if it exists)
  221. if ( pServerEntity == m_pVehicle )
  222. return false;
  223. if ( pEntity->GetHealth() > 0 )
  224. {
  225. CBreakable *pBreakable = dynamic_cast<CBreakable *>(pEntity);
  226. if ( pBreakable && pBreakable->IsBreakable() && pBreakable->GetMaterialType() == matGlass)
  227. {
  228. return false;
  229. }
  230. }
  231. return BaseClass::ShouldHitEntity( pServerEntity, contentsMask );
  232. }
  233. private:
  234. CBaseEntity *m_pVehicle;
  235. };
  236. //-----------------------------------------------------------------------------
  237. // Purpose: Check the weapon LOS for an owner at an arbitrary position
  238. // If bSetConditions is true, LOS related conditions will also be set
  239. //-----------------------------------------------------------------------------
  240. bool CBaseCombatWeapon::WeaponLOSCondition( const Vector &ownerPos, const Vector &targetPos, bool bSetConditions )
  241. {
  242. // --------------------
  243. // Check for occlusion
  244. // --------------------
  245. CAI_BaseNPC* npcOwner = m_hOwner.Get()->MyNPCPointer();
  246. // Find its relative shoot position
  247. Vector vecRelativeShootPosition;
  248. VectorSubtract( npcOwner->Weapon_ShootPosition(), npcOwner->GetAbsOrigin(), vecRelativeShootPosition );
  249. Vector barrelPos = ownerPos + vecRelativeShootPosition;
  250. // FIXME: If we're in a vehicle, we need some sort of way to handle shooting out of them
  251. // Use the custom LOS trace filter
  252. CWeaponLOSFilter traceFilter( m_hOwner.Get(), npcOwner->GetEnemy(), COLLISION_GROUP_BREAKABLE_GLASS );
  253. trace_t tr;
  254. UTIL_TraceLine( barrelPos, targetPos, MASK_SHOT, &traceFilter, &tr );
  255. // See if we completed the trace without interruption
  256. if ( tr.fraction == 1.0 )
  257. {
  258. if ( ai_debug_shoot_positions.GetBool() )
  259. {
  260. NDebugOverlay::Line( barrelPos, targetPos, 0, 255, 0, false, 1.0 );
  261. }
  262. return true;
  263. }
  264. CBaseEntity *pHitEnt = tr.m_pEnt;
  265. CBasePlayer *pEnemyPlayer = ToBasePlayer( npcOwner->GetEnemy() );
  266. // is player in a vehicle? if so, verify vehicle is target and return if so (so npc shoots at vehicle)
  267. if ( pEnemyPlayer && pEnemyPlayer->IsInAVehicle() )
  268. {
  269. // Ok, player in vehicle, check if vehicle is target we're looking at, fire if it is
  270. // Also, check to see if the owner of the entity is the vehicle, in which case it's valid too.
  271. // This catches vehicles that use bone followers.
  272. CBaseEntity *pVehicle = pEnemyPlayer->GetVehicle()->GetVehicleEnt();
  273. if ( pHitEnt == pVehicle || pHitEnt->GetOwnerEntity() == pVehicle )
  274. return true;
  275. }
  276. // Hitting our enemy is a success case
  277. if ( pHitEnt == npcOwner->GetEnemy() )
  278. {
  279. if ( ai_debug_shoot_positions.GetBool() )
  280. {
  281. NDebugOverlay::Line( barrelPos, targetPos, 0, 255, 0, false, 1.0 );
  282. }
  283. return true;
  284. }
  285. // If a vehicle is blocking the view, grab its driver and use that as the combat character
  286. CBaseCombatCharacter *pBCC;
  287. IServerVehicle *pVehicle = pHitEnt->GetServerVehicle();
  288. if ( pVehicle )
  289. {
  290. pBCC = pVehicle->GetPassenger( );
  291. }
  292. else
  293. {
  294. pBCC = ToBaseCombatCharacter( pHitEnt );
  295. }
  296. if ( pBCC )
  297. {
  298. if ( npcOwner->IRelationType( pBCC ) == D_HT )
  299. return true;
  300. if ( bSetConditions )
  301. {
  302. npcOwner->SetCondition( COND_WEAPON_BLOCKED_BY_FRIEND );
  303. }
  304. }
  305. else if ( bSetConditions )
  306. {
  307. npcOwner->SetCondition( COND_WEAPON_SIGHT_OCCLUDED );
  308. npcOwner->SetEnemyOccluder( pHitEnt );
  309. if( ai_debug_shoot_positions.GetBool() )
  310. {
  311. NDebugOverlay::Line( tr.startpos, tr.endpos, 255, 0, 0, false, 1.0 );
  312. }
  313. }
  314. return false;
  315. }
  316. //-----------------------------------------------------------------------------
  317. // Purpose: Base class always returns not bits
  318. //-----------------------------------------------------------------------------
  319. int CBaseCombatWeapon::WeaponRangeAttack1Condition( float flDot, float flDist )
  320. {
  321. if ( UsesPrimaryAmmo() && !HasPrimaryAmmo() )
  322. {
  323. return COND_NO_PRIMARY_AMMO;
  324. }
  325. else if ( flDist < m_fMinRange1)
  326. {
  327. return COND_TOO_CLOSE_TO_ATTACK;
  328. }
  329. else if (flDist > m_fMaxRange1)
  330. {
  331. return COND_TOO_FAR_TO_ATTACK;
  332. }
  333. else if (flDot < 0.5) // UNDONE: Why check this here? Isn't the AI checking this already?
  334. {
  335. return COND_NOT_FACING_ATTACK;
  336. }
  337. return COND_CAN_RANGE_ATTACK1;
  338. }
  339. //-----------------------------------------------------------------------------
  340. // Purpose: Base class always returns not bits
  341. //-----------------------------------------------------------------------------
  342. int CBaseCombatWeapon::WeaponRangeAttack2Condition( float flDot, float flDist )
  343. {
  344. // currently disabled
  345. return COND_NONE;
  346. if ( m_bReloadsSingly )
  347. {
  348. if (m_iClip2 <=0)
  349. {
  350. return COND_NO_SECONDARY_AMMO;
  351. }
  352. else if ( flDist < m_fMinRange2)
  353. {
  354. return COND_TOO_CLOSE_TO_ATTACK;
  355. }
  356. else if (flDist > m_fMaxRange2)
  357. {
  358. return COND_TOO_FAR_TO_ATTACK;
  359. }
  360. else if (flDot < 0.5)
  361. {
  362. return COND_NOT_FACING_ATTACK;
  363. }
  364. return COND_CAN_RANGE_ATTACK2;
  365. }
  366. return COND_NONE;
  367. }
  368. //-----------------------------------------------------------------------------
  369. // Purpose: Base class always returns not bits
  370. //-----------------------------------------------------------------------------
  371. int CBaseCombatWeapon::WeaponMeleeAttack1Condition( float flDot, float flDist )
  372. {
  373. return COND_NONE;
  374. }
  375. //-----------------------------------------------------------------------------
  376. // Purpose: Base class always returns not bits
  377. //-----------------------------------------------------------------------------
  378. int CBaseCombatWeapon::WeaponMeleeAttack2Condition( float flDot, float flDist )
  379. {
  380. return COND_NONE;
  381. }
  382. //====================================================================================
  383. // WEAPON DROPPING / DESTRUCTION
  384. //====================================================================================
  385. void CBaseCombatWeapon::Delete( void )
  386. {
  387. SetTouch( NULL );
  388. // FIXME: why doesn't this just remove itself now?
  389. SetThink(&CBaseCombatWeapon::SUB_Remove);
  390. SetNextThink( gpGlobals->curtime + 0.1f );
  391. }
  392. void CBaseCombatWeapon::DestroyItem( void )
  393. {
  394. CBaseCombatCharacter *pOwner = m_hOwner.Get();
  395. if ( pOwner )
  396. {
  397. // if attached to a player, remove.
  398. pOwner->RemovePlayerItem( this );
  399. }
  400. Kill( );
  401. }
  402. void CBaseCombatWeapon::Kill( void )
  403. {
  404. SetTouch( NULL );
  405. // FIXME: why doesn't this just remove itself now?
  406. // FIXME: how is this different than Delete(), and why do they have the same code in them?
  407. SetThink(&CBaseCombatWeapon::SUB_Remove);
  408. SetNextThink( gpGlobals->curtime + 0.1f );
  409. }
  410. //====================================================================================
  411. // FALL TO GROUND
  412. //====================================================================================
  413. //-----------------------------------------------------------------------------
  414. // Purpose: Setup for the fall
  415. //-----------------------------------------------------------------------------
  416. void CBaseCombatWeapon::FallInit( void )
  417. {
  418. VPhysicsDestroyObject();
  419. if ( !VPhysicsInitNormal( SOLID_BBOX, GetSolidFlags() | FSOLID_TRIGGER, false ) )
  420. {
  421. SetMoveType( MOVETYPE_FLYGRAVITY );
  422. SetSolid( SOLID_BBOX );
  423. AddSolidFlags( FSOLID_TRIGGER );
  424. }
  425. else
  426. {
  427. #if !defined( CLIENT_DLL )
  428. // Constrained start?
  429. if ( HasSpawnFlags( SF_WEAPON_START_CONSTRAINED ) )
  430. {
  431. //Constrain the weapon in place
  432. IPhysicsObject *pReferenceObject, *pAttachedObject;
  433. pReferenceObject = g_PhysWorldObject;
  434. pAttachedObject = VPhysicsGetObject();
  435. if ( pReferenceObject && pAttachedObject )
  436. {
  437. constraint_fixedparams_t fixed;
  438. fixed.Defaults();
  439. fixed.InitWithCurrentObjectState( pReferenceObject, pAttachedObject );
  440. fixed.constraint.forceLimit = lbs2kg( 10000 );
  441. fixed.constraint.torqueLimit = lbs2kg( 10000 );
  442. m_pConstraint = physenv->CreateFixedConstraint( pReferenceObject, pAttachedObject, NULL, fixed );
  443. m_pConstraint->SetGameData( (void *) this );
  444. }
  445. }
  446. #endif //CLIENT_DLL
  447. }
  448. SetPickupTouch();
  449. SetThink( &CBaseCombatWeapon::FallThink );
  450. SetNextThink( gpGlobals->curtime + 0.1f );
  451. }
  452. //-----------------------------------------------------------------------------
  453. // Purpose: Items that have just spawned run this think to catch them when
  454. // they hit the ground. Once we're sure that the object is grounded,
  455. // we change its solid type to trigger and set it in a large box that
  456. // helps the player get it.
  457. //-----------------------------------------------------------------------------
  458. void CBaseCombatWeapon::FallThink ( void )
  459. {
  460. SetNextThink( gpGlobals->curtime + 0.1f );
  461. bool shouldMaterialize = false;
  462. IPhysicsObject *pPhysics = VPhysicsGetObject();
  463. if ( pPhysics )
  464. {
  465. shouldMaterialize = pPhysics->IsAsleep();
  466. }
  467. else
  468. {
  469. shouldMaterialize = (GetFlags() & FL_ONGROUND) ? true : false;
  470. }
  471. if ( shouldMaterialize )
  472. {
  473. // clatter if we have an owner (i.e., dropped by someone)
  474. // don't clatter if the gun is waiting to respawn (if it's waiting, it is invisible!)
  475. if ( GetOwnerEntity() )
  476. {
  477. EmitSound( "BaseCombatWeapon.WeaponDrop" );
  478. }
  479. Materialize();
  480. }
  481. }
  482. //====================================================================================
  483. // WEAPON SPAWNING
  484. //====================================================================================
  485. //-----------------------------------------------------------------------------
  486. // Purpose: Make a weapon visible and tangible
  487. //-----------------------------------------------------------------------------//
  488. void CBaseCombatWeapon::Materialize( void )
  489. {
  490. if ( IsEffectActive( EF_NODRAW ) )
  491. {
  492. // changing from invisible state to visible.
  493. EmitSound( "BaseCombatWeapon.WeaponMaterialize" );
  494. RemoveEffects( EF_NODRAW );
  495. DoMuzzleFlash();
  496. }
  497. SetSolid( SOLID_BBOX );
  498. AddSolidFlags( FSOLID_TRIGGER );
  499. SetPickupTouch();
  500. SetThink (NULL);
  501. }
  502. //-----------------------------------------------------------------------------
  503. // Purpose: See if the game rules will let this weapon respawn
  504. //-----------------------------------------------------------------------------
  505. void CBaseCombatWeapon::AttemptToMaterialize( void )
  506. {
  507. float time = g_pGameRules->FlWeaponTryRespawn( this );
  508. if ( time == 0 )
  509. {
  510. Materialize();
  511. return;
  512. }
  513. SetNextThink( gpGlobals->curtime + time );
  514. }
  515. //-----------------------------------------------------------------------------
  516. // Purpose: Weapon has been picked up, should it respawn?
  517. //-----------------------------------------------------------------------------
  518. void CBaseCombatWeapon::CheckRespawn( void )
  519. {
  520. switch ( g_pGameRules->WeaponShouldRespawn( this ) )
  521. {
  522. case GR_WEAPON_RESPAWN_YES:
  523. Respawn();
  524. break;
  525. case GR_WEAPON_RESPAWN_NO:
  526. return;
  527. break;
  528. }
  529. }
  530. //-----------------------------------------------------------------------------
  531. // Purpose:
  532. //-----------------------------------------------------------------------------
  533. int CBaseCombatWeapon::ObjectCaps( void )
  534. {
  535. int caps = BaseClass::ObjectCaps();
  536. if ( !IsFollowingEntity() && !HasSpawnFlags(SF_WEAPON_NO_PLAYER_PICKUP) )
  537. {
  538. caps |= FCAP_IMPULSE_USE;
  539. }
  540. return caps;
  541. }
  542. //-----------------------------------------------------------------------------
  543. // Purpose:
  544. //-----------------------------------------------------------------------------
  545. void CBaseCombatWeapon::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
  546. {
  547. CBasePlayer *pPlayer = ToBasePlayer( pActivator );
  548. if ( pPlayer )
  549. {
  550. m_OnPlayerUse.FireOutput( pActivator, pCaller );
  551. //
  552. // Bump the weapon to try equipping it before picking it up physically. This is
  553. // important in a few spots in the game where the player could potentially +use pickup
  554. // and then THROW AWAY a vital weapon, rendering them unable to continue the game.
  555. //
  556. if ( pPlayer->BumpWeapon( this ) )
  557. {
  558. OnPickedUp( pPlayer );
  559. }
  560. #if defined(PORTAL2)
  561. else if ( dynamic_cast<CWeaponPortalgun*>( this ) == NULL )
  562. {
  563. pPlayer->PickupObject( this );
  564. }
  565. #endif
  566. }
  567. }
  568. void CBaseCombatWeapon::MakeWeaponNameFromEntity( CBaseEntity *pOther )
  569. {
  570. // If I have a name, make my weapon match it with "_weapon" appended
  571. if ( pOther->GetEntityName() != NULL_STRING )
  572. {
  573. const char *pMarineName = STRING( pOther->GetEntityName() );
  574. const char *pError = UTIL_VarArgs( "%s_weapon", pMarineName );
  575. string_t pooledName = AllocPooledString( pError );
  576. SetName( pooledName );
  577. }
  578. }
  579. //-----------------------------------------------------------------------------
  580. // Purpose: Randomizes weapon model bodygroups if that have the
  581. // keyword 'module_slot', and sets the viewmodel to correspond.
  582. //-----------------------------------------------------------------------------
  583. void CBaseCombatWeapon::SetWeaponModules( void )
  584. {
  585. if (IsViewModel())
  586. return;
  587. //early out if we've checked this model for modular bodygroups before and found none
  588. if (m_iWeaponModule == MODULAR_BODYGROUPS_NONE_AVAILABLE)
  589. return;
  590. MDLCACHE_CRITICAL_SECTION();
  591. //init module bodygroups on new weapons
  592. if ( m_iWeaponModule == MODULAR_BODYGROUPS_DEFAULT_NONE_SET )
  593. {
  594. m_iWeaponModule = MODULAR_BODYGROUPS_NONE_AVAILABLE;
  595. for ( int n=0; n<GetNumBodyGroups(); n++ )
  596. {
  597. if ( V_strstr( GetBodygroupName( n ), "module_slot" ) != NULL )
  598. {
  599. if ( !RandomInt( 0, 4 ) ) // each bodygroup gets a 20% chance to roll
  600. {
  601. SetBodygroup( n, RandomInt( 0, GetBodygroupCount( n ) - 1 ) );
  602. m_iWeaponModule = MODULAR_BODYGROUPS_RANDOMIZED;
  603. }
  604. }
  605. }
  606. if (m_iWeaponModule == MODULAR_BODYGROUPS_NONE_AVAILABLE)
  607. return;
  608. }
  609. //set viewmodel to match
  610. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  611. if ( !pOwner )
  612. return;
  613. CBaseViewModel *vm = pOwner->GetViewModel( m_nViewModelIndex );
  614. if ( vm && m_iWeaponModule == MODULAR_BODYGROUPS_RANDOMIZED && vm->m_nBody != m_nBody )
  615. vm->m_nBody = m_nBody;
  616. }