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

734 lines
22 KiB

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