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.

1700 lines
46 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "Sprite.h"
  9. #include "EnvLaser.h"
  10. #include "basecombatweapon.h"
  11. #include "explode.h"
  12. #include "eventqueue.h"
  13. #include "gamerules.h"
  14. #include "ammodef.h"
  15. #include "in_buttons.h"
  16. #include "soundent.h"
  17. #include "ndebugoverlay.h"
  18. #include "grenade_beam.h"
  19. #include "vstdlib/random.h"
  20. #include "engine/IEngineSound.h"
  21. #include "triggers.h"
  22. #include "physics_cannister.h"
  23. #include "player.h"
  24. #include "entitylist.h"
  25. #define SF_TANK_ACTIVE 0x0001
  26. #define SF_TANK_PLAYER 0x0002
  27. #define SF_TANK_HUMANS 0x0004
  28. #define SF_TANK_ALIENS 0x0008
  29. #define SF_TANK_LINEOFSIGHT 0x0010
  30. #define SF_TANK_CANCONTROL 0x0020
  31. #define SF_TANK_DAMAGE_KICK 0x0040 // Kick when take damage
  32. #define SF_TANK_AIM_AT_POS 0x0080 // Aim at a particular position
  33. #define SF_TANK_SOUNDON 0x8000
  34. enum TANKBULLET
  35. {
  36. TANK_BULLET_NONE = 0,
  37. TANK_BULLET_SMALL = 1,
  38. TANK_BULLET_MEDIUM = 2,
  39. TANK_BULLET_LARGE = 3,
  40. };
  41. #define FUNCTANK_FIREVOLUME 1000
  42. // Custom damage
  43. // env_laser (duration is 0.5 rate of fire)
  44. // rockets
  45. // explosion?
  46. class CFuncTank : public CBaseEntity
  47. {
  48. DECLARE_CLASS( CFuncTank, CBaseEntity );
  49. public:
  50. ~CFuncTank( void );
  51. void Spawn( void );
  52. void Activate( void );
  53. void Precache( void );
  54. bool CreateVPhysics( void );
  55. bool KeyValue( const char *szKeyName, const char *szValue );
  56. int ObjectCaps( void );
  57. void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
  58. void Think( void );
  59. void TrackTarget( void );
  60. int DrawDebugTextOverlays(void);
  61. virtual void FiringSequence( const Vector &barrelEnd, const Vector &forward, CBaseEntity *pAttacker );
  62. virtual void Fire( int bulletCount, const Vector &barrelEnd, const Vector &forward, CBaseEntity *pAttacker );
  63. void StartRotSound( void );
  64. void StopRotSound( void );
  65. inline bool IsActive( void ) { return (m_spawnflags & SF_TANK_ACTIVE)?TRUE:FALSE; }
  66. // Input handlers.
  67. void InputActivate( inputdata_t &inputdata );
  68. void InputDeactivate( inputdata_t &inputdata );
  69. void InputSetFireRate( inputdata_t &inputdata );
  70. void InputSetTargetPosition( inputdata_t &inputdata );
  71. void InputSetTargetEntityName( inputdata_t &inputdata );
  72. void InputSetTargetEntity( inputdata_t &inputdata );
  73. void TankActivate(void);
  74. void TankDeactivate(void);
  75. inline bool CanFire( void );
  76. bool InRange( float range );
  77. void TankTrace( const Vector &vecStart, const Vector &vecForward, const Vector &vecSpread, trace_t &tr );
  78. void TraceAttack( CBaseEntity *pAttacker, float flDamage, const Vector &vecDir, trace_t *ptr, int bitsDamageType);
  79. Vector WorldBarrelPosition( void )
  80. {
  81. EntityMatrix tmp;
  82. tmp.InitFromEntity( this );
  83. return tmp.LocalToWorld( m_barrelPos );
  84. }
  85. void UpdateMatrix( void )
  86. {
  87. m_parentMatrix.InitFromEntity( GetParent() ? GetParent() : NULL );
  88. }
  89. QAngle AimBarrelAt( const Vector &parentTarget );
  90. bool ShouldSavePhysics() { return false; }
  91. DECLARE_DATADESC();
  92. bool OnControls( CBaseEntity *pTest );
  93. bool StartControl( CBasePlayer *pController );
  94. void StopControl( void );
  95. void ControllerPostFrame( void );
  96. CBaseEntity *FindTarget( string_t targetName, CBaseEntity *pActivator );
  97. protected:
  98. CBasePlayer* m_pController;
  99. float m_flNextAttack;
  100. Vector m_vecControllerUsePos;
  101. float m_yawCenter; // "Center" yaw
  102. float m_yawRate; // Max turn rate to track targets
  103. float m_yawRange; // Range of turning motion (one-sided: 30 is +/- 30 degress from center)
  104. // Zero is full rotation
  105. float m_yawTolerance; // Tolerance angle
  106. float m_pitchCenter; // "Center" pitch
  107. float m_pitchRate; // Max turn rate on pitch
  108. float m_pitchRange; // Range of pitch motion as above
  109. float m_pitchTolerance; // Tolerance angle
  110. float m_fireLast; // Last time I fired
  111. float m_fireRate; // How many rounds/second
  112. float m_lastSightTime;// Last time I saw target
  113. float m_persist; // Persistence of firing (how long do I shoot when I can't see)
  114. float m_persist2; // Secondary persistence of firing (randomly shooting when I can't see)
  115. float m_persist2burst;// How long secondary persistence burst lasts
  116. float m_minRange; // Minimum range to aim/track
  117. float m_maxRange; // Max range to aim/track
  118. Vector m_barrelPos; // Length of the freakin barrel
  119. float m_spriteScale; // Scale of any sprites we shoot
  120. string_t m_iszSpriteSmoke;
  121. string_t m_iszSpriteFlash;
  122. TANKBULLET m_bulletType; // Bullet type
  123. int m_iBulletDamage; // 0 means use Bullet type's default damage
  124. Vector m_sightOrigin; // Last sight of target
  125. int m_spread; // firing spread
  126. string_t m_iszMaster; // Master entity (game_team_master or multisource)
  127. int m_iSmallAmmoType;
  128. int m_iMediumAmmoType;
  129. int m_iLargeAmmoType;
  130. string_t m_soundStartRotate;
  131. string_t m_soundStopRotate;
  132. string_t m_soundLoopRotate;
  133. string_t m_targetEntityName;
  134. EHANDLE m_hTarget;
  135. Vector m_vTargetPosition;
  136. EntityMatrix m_parentMatrix;
  137. COutputEvent m_OnFire;
  138. COutputEvent m_OnLoseTarget;
  139. COutputEvent m_OnAquireTarget;
  140. CHandle<CBaseTrigger> m_hControlVolume;
  141. string_t m_iszControlVolume;
  142. };
  143. BEGIN_DATADESC( CFuncTank )
  144. DEFINE_KEYFIELD( m_yawRate, FIELD_FLOAT, "yawrate" ),
  145. DEFINE_KEYFIELD( m_yawRange, FIELD_FLOAT, "yawrange" ),
  146. DEFINE_KEYFIELD( m_yawTolerance, FIELD_FLOAT, "yawtolerance" ),
  147. DEFINE_KEYFIELD( m_pitchRate, FIELD_FLOAT, "pitchrate" ),
  148. DEFINE_KEYFIELD( m_pitchRange, FIELD_FLOAT, "pitchrange" ),
  149. DEFINE_KEYFIELD( m_pitchTolerance, FIELD_FLOAT, "pitchtolerance" ),
  150. DEFINE_KEYFIELD( m_fireRate, FIELD_FLOAT, "firerate" ),
  151. DEFINE_KEYFIELD( m_persist, FIELD_FLOAT, "persistence" ),
  152. DEFINE_KEYFIELD( m_persist2, FIELD_FLOAT, "persistence2" ),
  153. DEFINE_KEYFIELD( m_minRange, FIELD_FLOAT, "minRange" ),
  154. DEFINE_KEYFIELD( m_maxRange, FIELD_FLOAT, "maxRange" ),
  155. DEFINE_KEYFIELD( m_spriteScale, FIELD_FLOAT, "spritescale" ),
  156. DEFINE_KEYFIELD( m_iszSpriteSmoke, FIELD_STRING, "spritesmoke" ),
  157. DEFINE_KEYFIELD( m_iszSpriteFlash, FIELD_STRING, "spriteflash" ),
  158. DEFINE_KEYFIELD( m_bulletType, FIELD_INTEGER, "bullet" ),
  159. DEFINE_KEYFIELD( m_spread, FIELD_INTEGER, "firespread" ),
  160. DEFINE_KEYFIELD( m_iBulletDamage, FIELD_INTEGER, "bullet_damage" ),
  161. DEFINE_KEYFIELD( m_iszMaster, FIELD_STRING, "master" ),
  162. DEFINE_KEYFIELD( m_soundStartRotate, FIELD_SOUNDNAME, "rotatestartsound" ),
  163. DEFINE_KEYFIELD( m_soundStopRotate, FIELD_SOUNDNAME, "rotatestopsound" ),
  164. DEFINE_KEYFIELD( m_soundLoopRotate, FIELD_SOUNDNAME, "rotatesound" ),
  165. DEFINE_KEYFIELD( m_iszControlVolume, FIELD_STRING, "control_volume" ),
  166. DEFINE_FIELD( m_yawCenter, FIELD_FLOAT ),
  167. DEFINE_FIELD( m_pitchCenter, FIELD_FLOAT ),
  168. DEFINE_FIELD( m_fireLast, FIELD_TIME ),
  169. DEFINE_FIELD( m_lastSightTime, FIELD_TIME ),
  170. DEFINE_FIELD( m_barrelPos, FIELD_VECTOR ),
  171. DEFINE_FIELD( m_sightOrigin, FIELD_VECTOR ),
  172. DEFINE_FIELD( m_pController, FIELD_CLASSPTR ),
  173. DEFINE_FIELD( m_vecControllerUsePos, FIELD_VECTOR ),
  174. DEFINE_FIELD( m_flNextAttack, FIELD_TIME ),
  175. DEFINE_FIELD( m_targetEntityName, FIELD_STRING ),
  176. DEFINE_FIELD( m_vTargetPosition, FIELD_VECTOR ),
  177. DEFINE_FIELD( m_persist2burst, FIELD_FLOAT),
  178. //DEFINE_FIELD( m_parentMatrix, FIELD_MATRIX ), // DON'T SAVE
  179. DEFINE_FIELD( m_hTarget, FIELD_EHANDLE ),
  180. DEFINE_FIELD( m_hControlVolume, FIELD_EHANDLE ),
  181. // Do not save these.
  182. //DEFINE_FIELD( m_iSmallAmmoType, FIELD_INTEGER ),
  183. //DEFINE_FIELD( m_iMediumAmmoType, FIELD_INTEGER ),
  184. //DEFINE_FIELD( m_iLargeAmmoType, FIELD_INTEGER ),
  185. // Inputs
  186. DEFINE_INPUTFUNC( FIELD_VOID, "Activate", InputActivate ),
  187. DEFINE_INPUTFUNC( FIELD_VOID, "Deactivate", InputDeactivate ),
  188. DEFINE_INPUTFUNC( FIELD_FLOAT, "SetFireRate", InputSetFireRate ),
  189. DEFINE_INPUTFUNC( FIELD_VECTOR, "SetTargetPosition", InputSetTargetPosition ),
  190. DEFINE_INPUTFUNC( FIELD_STRING, "SetTargetEntityName", InputSetTargetEntityName ),
  191. DEFINE_INPUTFUNC( FIELD_EHANDLE, "SetTargetEntity", InputSetTargetEntity ),
  192. // Outputs
  193. DEFINE_OUTPUT(m_OnFire, "OnFire"),
  194. DEFINE_OUTPUT(m_OnLoseTarget, "OnLoseTarget"),
  195. DEFINE_OUTPUT(m_OnAquireTarget, "OnAquireTarget"),
  196. END_DATADESC()
  197. //-----------------------------------------------------------------------------
  198. // Purpose:
  199. //-----------------------------------------------------------------------------
  200. CFuncTank::~CFuncTank( void )
  201. {
  202. if ( m_soundLoopRotate != NULL_STRING && (m_spawnflags & SF_TANK_SOUNDON) )
  203. {
  204. StopSound( entindex(), CHAN_STATIC, STRING(m_soundLoopRotate) );
  205. }
  206. }
  207. int CFuncTank::ObjectCaps( void )
  208. {
  209. int iCaps = BaseClass::ObjectCaps();
  210. if ( m_spawnflags & SF_TANK_CANCONTROL )
  211. {
  212. iCaps |= FCAP_IMPULSE_USE;
  213. }
  214. return iCaps;
  215. }
  216. //------------------------------------------------------------------------------
  217. // Purpose:
  218. //------------------------------------------------------------------------------
  219. inline bool CFuncTank::CanFire( void )
  220. {
  221. float flTimeDelay = gpGlobals->curtime - m_lastSightTime;
  222. // Fire when can't see enemy if time is less that persistence time
  223. if ( flTimeDelay <= m_persist)
  224. {
  225. return true;
  226. }
  227. // Fire when I'm in a persistence2 burst
  228. else if (flTimeDelay <= m_persist2burst)
  229. {
  230. return true;
  231. }
  232. // If less than persistence2, occasionally do another burst
  233. else if (flTimeDelay <= m_persist2)
  234. {
  235. if (random->RandomInt(0,30)==0)
  236. {
  237. m_persist2burst = flTimeDelay+0.5;
  238. return true;
  239. }
  240. }
  241. return false;
  242. }
  243. //------------------------------------------------------------------------------
  244. // Purpose: Input handler for activating the tank.
  245. //------------------------------------------------------------------------------
  246. void CFuncTank::InputActivate( inputdata_t &inputdata )
  247. {
  248. TankActivate();
  249. }
  250. //-----------------------------------------------------------------------------
  251. // Purpose:
  252. //-----------------------------------------------------------------------------
  253. void CFuncTank::TankActivate(void)
  254. {
  255. m_spawnflags |= SF_TANK_ACTIVE;
  256. SetNextThink( gpGlobals->curtime + 0.1f );
  257. m_fireLast = 0;
  258. }
  259. //-----------------------------------------------------------------------------
  260. // Purpose: Input handler for deactivating the tank.
  261. //-----------------------------------------------------------------------------
  262. void CFuncTank::InputDeactivate( inputdata_t &inputdata )
  263. {
  264. TankDeactivate();
  265. }
  266. //-----------------------------------------------------------------------------
  267. // Purpose:
  268. //-----------------------------------------------------------------------------
  269. void CFuncTank::TankDeactivate(void)
  270. {
  271. m_spawnflags &= ~SF_TANK_ACTIVE;
  272. m_fireLast = 0;
  273. StopRotSound();
  274. }
  275. //-----------------------------------------------------------------------------
  276. // Purpose: Input handler for changing the name of the tank's target entity.
  277. //-----------------------------------------------------------------------------
  278. void CFuncTank::InputSetTargetEntityName( inputdata_t &inputdata )
  279. {
  280. m_targetEntityName = inputdata.value.StringID();
  281. m_hTarget = FindTarget( m_targetEntityName, inputdata.pActivator );
  282. // No longer aim at target position if have one
  283. m_spawnflags &= ~SF_TANK_AIM_AT_POS;
  284. }
  285. //-----------------------------------------------------------------------------
  286. // Purpose: Input handler for setting a new target entity by ehandle.
  287. //-----------------------------------------------------------------------------
  288. void CFuncTank::InputSetTargetEntity( inputdata_t &inputdata )
  289. {
  290. if (inputdata.value.Entity() != NULL)
  291. {
  292. m_targetEntityName = inputdata.value.Entity()->GetEntityName();
  293. }
  294. else
  295. {
  296. m_targetEntityName = NULL_STRING;
  297. }
  298. m_hTarget = inputdata.value.Entity();
  299. // No longer aim at target position if have one
  300. m_spawnflags &= ~SF_TANK_AIM_AT_POS;
  301. }
  302. //-----------------------------------------------------------------------------
  303. // Purpose: Input handler for setting the rate of fire in shots per second.
  304. //-----------------------------------------------------------------------------
  305. void CFuncTank::InputSetFireRate( inputdata_t &inputdata )
  306. {
  307. m_fireRate = inputdata.value.Float();
  308. }
  309. //-----------------------------------------------------------------------------
  310. // Purpose: Input handler for setting the target as a position.
  311. //-----------------------------------------------------------------------------
  312. void CFuncTank::InputSetTargetPosition( inputdata_t &inputdata )
  313. {
  314. m_spawnflags |= SF_TANK_AIM_AT_POS;
  315. m_hTarget = NULL;
  316. inputdata.value.Vector3D(m_vTargetPosition);
  317. }
  318. //-----------------------------------------------------------------------------
  319. // Purpose: Draw any debug text overlays
  320. // Output : Current text offset from the top
  321. //-----------------------------------------------------------------------------
  322. int CFuncTank::DrawDebugTextOverlays(void)
  323. {
  324. int text_offset = BaseClass::DrawDebugTextOverlays();
  325. if (m_debugOverlays & OVERLAY_TEXT_BIT)
  326. {
  327. // --------------
  328. // State
  329. // --------------
  330. char tempstr[255];
  331. if (IsActive())
  332. {
  333. Q_strncpy(tempstr,"State: Active",sizeof(tempstr));
  334. }
  335. else
  336. {
  337. Q_strncpy(tempstr,"State: Inactive",sizeof(tempstr));
  338. }
  339. EntityText(text_offset,tempstr,0);
  340. text_offset++;
  341. // -------------------
  342. // Print Firing Speed
  343. // --------------------
  344. Q_snprintf(tempstr,sizeof(tempstr),"Fire Rate: %f",m_fireRate);
  345. EntityText(text_offset,tempstr,0);
  346. text_offset++;
  347. // --------------
  348. // Print Target
  349. // --------------
  350. if (m_hTarget!=NULL)
  351. {
  352. Q_snprintf(tempstr,sizeof(tempstr),"Target: %s",m_hTarget->GetDebugName());
  353. }
  354. else
  355. {
  356. Q_snprintf(tempstr,sizeof(tempstr),"Target: - ");
  357. }
  358. EntityText(text_offset,tempstr,0);
  359. text_offset++;
  360. // --------------
  361. // Target Pos
  362. // --------------
  363. if (m_spawnflags & SF_TANK_AIM_AT_POS)
  364. {
  365. Q_snprintf(tempstr,sizeof(tempstr),"Aim Pos: %3.0f %3.0f %3.0f",m_vTargetPosition.x,m_vTargetPosition.y,m_vTargetPosition.z);
  366. }
  367. else
  368. {
  369. Q_snprintf(tempstr,sizeof(tempstr),"Aim Pos: - ");
  370. }
  371. EntityText(text_offset,tempstr,0);
  372. text_offset++;
  373. }
  374. return text_offset;
  375. }
  376. //-----------------------------------------------------------------------------
  377. // Purpose:
  378. // Input : pAttacker -
  379. // flDamage -
  380. // vecDir -
  381. // ptr -
  382. // bitsDamageType -
  383. //-----------------------------------------------------------------------------
  384. void CFuncTank::TraceAttack( CBaseEntity *pAttacker, float flDamage, const Vector &vecDir, trace_t *ptr, int bitsDamageType)
  385. {
  386. if (m_spawnflags & SF_TANK_DAMAGE_KICK)
  387. {
  388. // Deflect the func_tank
  389. // Only adjust yaw for now
  390. if (pAttacker)
  391. {
  392. Vector vFromAttacker = (pAttacker->EyePosition()-GetAbsOrigin());
  393. vFromAttacker.z = 0;
  394. VectorNormalize(vFromAttacker);
  395. Vector vFromAttacker2 = (ptr->endpos-GetAbsOrigin());
  396. vFromAttacker2.z = 0;
  397. VectorNormalize(vFromAttacker2);
  398. Vector vCrossProduct;
  399. CrossProduct(vFromAttacker,vFromAttacker2, vCrossProduct);
  400. Msg( "%f\n",vCrossProduct.z);
  401. QAngle angles;
  402. angles = GetLocalAngles();
  403. if (vCrossProduct.z > 0)
  404. {
  405. angles.y += 10;
  406. }
  407. else
  408. {
  409. angles.y -= 10;
  410. }
  411. // Limit against range in y
  412. if ( angles.y > m_yawCenter + m_yawRange )
  413. {
  414. angles.y = m_yawCenter + m_yawRange;
  415. }
  416. else if ( angles.y < (m_yawCenter - m_yawRange) )
  417. {
  418. angles.y = (m_yawCenter - m_yawRange);
  419. }
  420. SetLocalAngles( angles );
  421. }
  422. }
  423. }
  424. //-----------------------------------------------------------------------------
  425. // Purpose:
  426. // Input : targetName -
  427. // pActivator -
  428. //-----------------------------------------------------------------------------
  429. CBaseEntity *CFuncTank::FindTarget( string_t targetName, CBaseEntity *pActivator )
  430. {
  431. return gEntList.FindEntityGenericNearest( STRING( targetName ), GetAbsOrigin(), 0, this, pActivator );
  432. }
  433. //-----------------------------------------------------------------------------
  434. // Purpose: Caches entity key values until spawn is called.
  435. // Input : szKeyName -
  436. // szValue -
  437. // Output :
  438. //-----------------------------------------------------------------------------
  439. bool CFuncTank::KeyValue( const char *szKeyName, const char *szValue )
  440. {
  441. if (FStrEq(szKeyName, "barrel"))
  442. {
  443. m_barrelPos.x = atof(szValue);
  444. }
  445. else if (FStrEq(szKeyName, "barrely"))
  446. {
  447. m_barrelPos.y = atof(szValue);
  448. }
  449. else if (FStrEq(szKeyName, "barrelz"))
  450. {
  451. m_barrelPos.z = atof(szValue);
  452. }
  453. else
  454. return BaseClass::KeyValue( szKeyName, szValue );
  455. return true;
  456. }
  457. static Vector gTankSpread[] =
  458. {
  459. Vector( 0, 0, 0 ), // perfect
  460. Vector( 0.025, 0.025, 0.025 ), // small cone
  461. Vector( 0.05, 0.05, 0.05 ), // medium cone
  462. Vector( 0.1, 0.1, 0.1 ), // large cone
  463. Vector( 0.25, 0.25, 0.25 ), // extra-large cone
  464. };
  465. #define MAX_FIRING_SPREADS ARRAYSIZE(gTankSpread)
  466. void CFuncTank::Spawn( void )
  467. {
  468. Precache();
  469. SetMoveType( MOVETYPE_PUSH ); // so it doesn't get pushed by anything
  470. SetSolid( SOLID_VPHYSICS );
  471. SetModel( STRING( GetModelName() ) );
  472. m_yawCenter = GetLocalAngles().y;
  473. m_pitchCenter = GetLocalAngles().x;
  474. m_vTargetPosition = vec3_origin;
  475. if ( IsActive() )
  476. SetNextThink( gpGlobals->curtime + 1.0f );
  477. UpdateMatrix();
  478. m_sightOrigin = WorldBarrelPosition(); // Point at the end of the barrel
  479. if ( m_spread > MAX_FIRING_SPREADS )
  480. {
  481. m_spread = 0;
  482. }
  483. // No longer aim at target position if have one
  484. m_spawnflags &= ~SF_TANK_AIM_AT_POS;
  485. if (m_spawnflags & SF_TANK_DAMAGE_KICK)
  486. {
  487. m_takedamage = DAMAGE_YES;
  488. }
  489. // UNDONE: Do this?
  490. //m_targetEntityName = m_target;
  491. CreateVPhysics();
  492. }
  493. //-----------------------------------------------------------------------------
  494. // Purpose:
  495. //-----------------------------------------------------------------------------
  496. void CFuncTank::Activate( void )
  497. {
  498. BaseClass::Activate();
  499. m_hControlVolume = NULL;
  500. // Find our control volume
  501. if ( m_iszControlVolume != NULL_STRING )
  502. {
  503. m_hControlVolume = dynamic_cast<CBaseTrigger*>( gEntList.FindEntityByName( NULL, m_iszControlVolume ) );
  504. }
  505. if (( !m_hControlVolume ) && (m_spawnflags & SF_TANK_CANCONTROL))
  506. {
  507. Msg( "ERROR: Couldn't find control volume for player-controllable func_tank %s.\n", STRING(GetEntityName()) );
  508. UTIL_Remove( this );
  509. }
  510. }
  511. bool CFuncTank::CreateVPhysics()
  512. {
  513. VPhysicsInitShadow( false, false );
  514. return true;
  515. }
  516. void CFuncTank::Precache( void )
  517. {
  518. m_iSmallAmmoType = GetAmmoDef()->Index("9mmRound");
  519. m_iMediumAmmoType = GetAmmoDef()->Index("9mmRound");
  520. m_iLargeAmmoType = GetAmmoDef()->Index("12mmRound");
  521. if ( m_iszSpriteSmoke != NULL_STRING )
  522. PrecacheModel( STRING(m_iszSpriteSmoke) );
  523. if ( m_iszSpriteFlash != NULL_STRING )
  524. PrecacheModel( STRING(m_iszSpriteFlash) );
  525. if ( m_soundStartRotate != NULL_STRING )
  526. PrecacheScriptSound( STRING(m_soundStartRotate) );
  527. if ( m_soundStopRotate != NULL_STRING )
  528. PrecacheScriptSound( STRING(m_soundStopRotate) );
  529. if ( m_soundLoopRotate != NULL_STRING )
  530. PrecacheScriptSound( STRING(m_soundLoopRotate) );
  531. }
  532. //==================================================================================
  533. // TANK CONTROLLING
  534. bool CFuncTank::OnControls( CBaseEntity *pTest )
  535. {
  536. if ( !(m_spawnflags & SF_TANK_CANCONTROL) )
  537. return FALSE;
  538. Vector offset = pTest->GetLocalOrigin() - GetLocalOrigin();
  539. if ( (m_vecControllerUsePos - pTest->GetLocalOrigin()).Length() < 30 )
  540. return TRUE;
  541. return FALSE;
  542. }
  543. bool CFuncTank::StartControl( CBasePlayer *pController )
  544. {
  545. if ( m_pController != NULL )
  546. return FALSE;
  547. // Team only or disabled?
  548. if ( m_iszMaster != NULL_STRING )
  549. {
  550. if ( !UTIL_IsMasterTriggered( m_iszMaster, pController ) )
  551. return FALSE;
  552. }
  553. // Holster player's weapon
  554. m_pController = pController;
  555. if ( m_pController->GetActiveWeapon() )
  556. {
  557. m_pController->GetActiveWeapon()->Holster();
  558. }
  559. m_pController->m_Local.m_iHideHUD |= HIDEHUD_WEAPONSELECTION;
  560. m_vecControllerUsePos = m_pController->GetLocalOrigin();
  561. SetNextThink( gpGlobals->curtime + 0.1f );
  562. return TRUE;
  563. }
  564. void CFuncTank::StopControl()
  565. {
  566. // TODO: bring back the controllers current weapon
  567. if ( !m_pController )
  568. return;
  569. if ( m_pController->GetActiveWeapon() )
  570. m_pController->GetActiveWeapon()->Deploy();
  571. m_pController->m_Local.m_iHideHUD &= ~HIDEHUD_WEAPONSELECTION;
  572. SetNextThink( TICK_NEVER_THINK );
  573. m_pController = NULL;
  574. if ( IsActive() )
  575. SetNextThink( gpGlobals->curtime + 1.0f );
  576. }
  577. // Called each frame by the player's ItemPostFrame
  578. void CFuncTank::ControllerPostFrame( void )
  579. {
  580. Assert(m_pController != NULL);
  581. if ( gpGlobals->curtime < m_flNextAttack )
  582. return;
  583. Vector forward;
  584. AngleVectors( GetAbsAngles(), &forward );
  585. if ( m_pController->m_nButtons & IN_ATTACK )
  586. {
  587. m_fireLast = gpGlobals->curtime - (1/m_fireRate) - 0.01; // to make sure the gun doesn't fire too many bullets
  588. int bulletCount = (gpGlobals->curtime - m_fireLast) * m_fireRate;
  589. Fire( bulletCount, WorldBarrelPosition(), forward, m_pController );
  590. // HACKHACK -- make some noise (that the AI can hear)
  591. CSoundEnt::InsertSound( SOUND_COMBAT, WorldSpaceCenter(), FUNCTANK_FIREVOLUME, 0.2 );
  592. m_flNextAttack = gpGlobals->curtime + (1/m_fireRate);
  593. }
  594. }
  595. void CFuncTank::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
  596. {
  597. if ( m_spawnflags & SF_TANK_CANCONTROL )
  598. {
  599. // player controlled turret
  600. CBasePlayer *pPlayer = ToBasePlayer( pActivator );
  601. if ( !pPlayer )
  602. return;
  603. if ( value == 2 && useType == USE_SET )
  604. {
  605. ControllerPostFrame();
  606. }
  607. else if ( !m_pController && useType != USE_OFF )
  608. {
  609. // The player must be within the func_tank controls
  610. Assert( m_hControlVolume );
  611. if ( !m_hControlVolume->IsTouching( pPlayer ) )
  612. return;
  613. pPlayer->SetUseEntity( this );
  614. StartControl( pPlayer );
  615. }
  616. else
  617. {
  618. StopControl();
  619. }
  620. }
  621. else
  622. {
  623. if ( !ShouldToggle( useType, IsActive() ) )
  624. return;
  625. if ( IsActive() )
  626. {
  627. TankDeactivate();
  628. }
  629. else
  630. {
  631. TankActivate();
  632. }
  633. }
  634. }
  635. bool CFuncTank::InRange( float range )
  636. {
  637. if ( range < m_minRange )
  638. return FALSE;
  639. if ( m_maxRange > 0 && range > m_maxRange )
  640. return FALSE;
  641. return TRUE;
  642. }
  643. void CFuncTank::Think( void )
  644. {
  645. // refresh the matrix
  646. UpdateMatrix();
  647. SetLocalAngularVelocity( vec3_angle );
  648. TrackTarget();
  649. if ( fabs(GetLocalAngularVelocity().x) > 1 || fabs(GetLocalAngularVelocity().y) > 1 )
  650. StartRotSound();
  651. else
  652. StopRotSound();
  653. }
  654. //-----------------------------------------------------------------------------
  655. // Purpose: Aim the offset barrel at a position in parent space
  656. // Input : parentTarget - the position of the target in parent space
  657. // Output : Vector - angles in local space
  658. //-----------------------------------------------------------------------------
  659. QAngle CFuncTank::AimBarrelAt( const Vector &parentTarget )
  660. {
  661. Vector target = parentTarget - GetLocalOrigin();
  662. float quadTarget = target.LengthSqr();
  663. float quadTargetXY = target.x*target.x + target.y*target.y;
  664. // Target is too close! Can't aim at it
  665. if ( quadTarget <= m_barrelPos.LengthSqr() )
  666. {
  667. return GetLocalAngles();
  668. }
  669. else
  670. {
  671. // We're trying to aim the offset barrel at an arbitrary point.
  672. // To calculate this, I think of the target as being on a sphere with
  673. // it's center at the origin of the gun.
  674. // The rotation we need is the opposite of the rotation that moves the target
  675. // along the surface of that sphere to intersect with the gun's shooting direction
  676. // To calculate that rotation, we simply calculate the intersection of the ray
  677. // coming out of the barrel with the target sphere (that's the new target position)
  678. // and use atan2() to get angles
  679. // angles from target pos to center
  680. float targetToCenterYaw = atan2( target.y, target.x );
  681. float centerToGunYaw = atan2( m_barrelPos.y, sqrt( quadTarget - (m_barrelPos.y*m_barrelPos.y) ) );
  682. float targetToCenterPitch = atan2( target.z, sqrt( quadTargetXY ) );
  683. float centerToGunPitch = atan2( -m_barrelPos.z, sqrt( quadTarget - (m_barrelPos.z*m_barrelPos.z) ) );
  684. return QAngle( -RAD2DEG(targetToCenterPitch+centerToGunPitch), RAD2DEG( targetToCenterYaw - centerToGunYaw ), 0 );
  685. }
  686. }
  687. void CFuncTank::TrackTarget( void )
  688. {
  689. trace_t tr;
  690. bool updateTime = FALSE, lineOfSight;
  691. QAngle angles;
  692. Vector barrelEnd;
  693. CBaseEntity *pTarget = NULL;
  694. barrelEnd.Init();
  695. // Get a position to aim for
  696. if (m_pController)
  697. {
  698. // Tanks attempt to mirror the player's angles
  699. angles = m_pController->EyeAngles();
  700. SetNextThink( gpGlobals->curtime + 0.05 );
  701. }
  702. else
  703. {
  704. if ( IsActive() )
  705. {
  706. SetNextThink( gpGlobals->curtime + 0.1f );
  707. }
  708. else
  709. {
  710. return;
  711. }
  712. // -----------------------------------
  713. // Get world target position
  714. // -----------------------------------
  715. barrelEnd = WorldBarrelPosition();
  716. Vector worldTargetPosition;
  717. if (m_spawnflags & SF_TANK_AIM_AT_POS)
  718. {
  719. worldTargetPosition = m_vTargetPosition;
  720. }
  721. else
  722. {
  723. CBaseEntity *pEntity = (CBaseEntity *)m_hTarget;
  724. if ( !pEntity || ( pEntity->GetFlags() & FL_NOTARGET ) )
  725. {
  726. if ( m_targetEntityName != NULL_STRING ) // New HL2 behavior
  727. {
  728. m_hTarget = FindTarget( m_targetEntityName, NULL );
  729. }
  730. else // HL1 style
  731. {
  732. m_hTarget = ToBasePlayer( GetContainingEntity( UTIL_FindClientInPVS( edict() ) ) );
  733. }
  734. if ( m_hTarget != NULL )
  735. {
  736. SetNextThink( gpGlobals->curtime ); // Think again immediately
  737. }
  738. else
  739. {
  740. if ( IsActive() )
  741. {
  742. SetNextThink( gpGlobals->curtime + 2 ); // Wait 2 secs
  743. }
  744. if ( m_fireLast !=0 )
  745. {
  746. m_OnLoseTarget.FireOutput(this, this);
  747. m_fireLast = 0;
  748. }
  749. }
  750. return;
  751. }
  752. pTarget = pEntity;
  753. // Calculate angle needed to aim at target
  754. worldTargetPosition = pEntity->EyePosition();
  755. }
  756. float range = (worldTargetPosition - barrelEnd).Length();
  757. if ( !InRange( range ) )
  758. {
  759. m_fireLast = 0;
  760. return;
  761. }
  762. UTIL_TraceLine( barrelEnd, worldTargetPosition, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );
  763. if (m_spawnflags & SF_TANK_AIM_AT_POS)
  764. {
  765. updateTime = TRUE;
  766. m_sightOrigin = m_vTargetPosition;
  767. }
  768. else
  769. {
  770. lineOfSight = FALSE;
  771. // No line of sight, don't track
  772. if ( tr.fraction == 1.0 || tr.m_pEnt == pTarget )
  773. {
  774. lineOfSight = TRUE;
  775. CBaseEntity *pInstance = pTarget;
  776. if ( InRange( range ) && pInstance && pInstance->IsAlive() )
  777. {
  778. updateTime = TRUE;
  779. // Sight position is BodyTarget with no noise (so gun doesn't bob up and down)
  780. m_sightOrigin = pInstance->BodyTarget( GetLocalOrigin(), false );
  781. }
  782. }
  783. }
  784. // Convert targetPosition to parent
  785. angles = AimBarrelAt( m_parentMatrix.WorldToLocal( m_sightOrigin ) );
  786. }
  787. // Force the angles to be relative to the center position
  788. float offsetY = UTIL_AngleDistance( angles.y, m_yawCenter );
  789. float offsetX = UTIL_AngleDistance( angles.x, m_pitchCenter );
  790. angles.y = m_yawCenter + offsetY;
  791. angles.x = m_pitchCenter + offsetX;
  792. // Limit against range in y
  793. // MDB - don't check pitch! If two func_tanks are meant to align,
  794. // and one can pitch and the other cannot, this can lead to them getting
  795. // different values for angles.y. Nothing is lost by not updating yaw
  796. // because the target is not in pitch range.
  797. bool bOutsideYawRange = ( fabs( offsetY ) > m_yawRange + m_yawTolerance );
  798. bool bOutsidePitchRange = ( fabs( offsetX ) > m_pitchRange + m_pitchTolerance );
  799. Vector vecToTarget = m_sightOrigin - GetLocalOrigin();
  800. // if target is outside yaw range
  801. if ( bOutsideYawRange )
  802. {
  803. if ( angles.y > m_yawCenter + m_yawRange )
  804. {
  805. angles.y = m_yawCenter + m_yawRange;
  806. }
  807. else if ( angles.y < (m_yawCenter - m_yawRange) )
  808. {
  809. angles.y = (m_yawCenter - m_yawRange);
  810. }
  811. }
  812. if ( bOutsidePitchRange || bOutsideYawRange || ( vecToTarget.Length() < ( barrelEnd - GetAbsOrigin() ).Length() ) )
  813. {
  814. // Don't update if you saw the player, but out of range
  815. updateTime = false;
  816. }
  817. if ( updateTime )
  818. {
  819. m_lastSightTime = gpGlobals->curtime;
  820. m_persist2burst = 0;
  821. }
  822. // Move toward target at rate or less
  823. float distY = UTIL_AngleDistance( angles.y, GetLocalAngles().y );
  824. QAngle vecAngVel = GetLocalAngularVelocity();
  825. vecAngVel.y = distY * 10;
  826. vecAngVel.y = clamp( vecAngVel.y, -m_yawRate, m_yawRate );
  827. // Limit against range in x
  828. angles.x = clamp( angles.x, m_pitchCenter - m_pitchRange, m_pitchCenter + m_pitchRange );
  829. // Move toward target at rate or less
  830. float distX = UTIL_AngleDistance( angles.x, GetLocalAngles().x );
  831. vecAngVel.x = distX * 10;
  832. vecAngVel.x = clamp( vecAngVel.x, -m_pitchRate, m_pitchRate );
  833. SetLocalAngularVelocity( vecAngVel );
  834. SetMoveDoneTime( 0.1 );
  835. if ( m_pController )
  836. return;
  837. if ( CanFire() && ( (fabs(distX) < m_pitchTolerance && fabs(distY) < m_yawTolerance) || (m_spawnflags & SF_TANK_LINEOFSIGHT) ) )
  838. {
  839. bool fire = FALSE;
  840. Vector forward;
  841. AngleVectors( GetLocalAngles(), &forward );
  842. forward = m_parentMatrix.ApplyRotation( forward );
  843. if ( m_spawnflags & SF_TANK_LINEOFSIGHT )
  844. {
  845. float length = (m_maxRange > 0) ? m_maxRange : MAX_TRACE_LENGTH;
  846. UTIL_TraceLine( barrelEnd, barrelEnd + forward * length, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );
  847. if ( tr.m_pEnt == pTarget )
  848. fire = TRUE;
  849. }
  850. else
  851. fire = TRUE;
  852. if ( fire )
  853. {
  854. if (m_fireLast == 0)
  855. {
  856. m_OnAquireTarget.FireOutput(this, this);
  857. }
  858. FiringSequence( barrelEnd, forward, this );
  859. }
  860. else
  861. {
  862. if (m_fireLast !=0)
  863. {
  864. m_OnLoseTarget.FireOutput(this, this);
  865. }
  866. m_fireLast = 0;
  867. }
  868. }
  869. else
  870. {
  871. if (m_fireLast !=0)
  872. {
  873. m_OnLoseTarget.FireOutput(this, this);
  874. }
  875. m_fireLast = 0;
  876. }
  877. }
  878. //-----------------------------------------------------------------------------
  879. // Purpose: Start of firing sequence. By default, just fire now.
  880. // Input : &barrelEnd -
  881. // &forward -
  882. // *pAttacker -
  883. //-----------------------------------------------------------------------------
  884. void CFuncTank::FiringSequence( const Vector &barrelEnd, const Vector &forward, CBaseEntity *pAttacker )
  885. {
  886. if ( m_fireLast != 0 )
  887. {
  888. int bulletCount = (gpGlobals->curtime - m_fireLast) * m_fireRate;
  889. if ( bulletCount > 0 )
  890. {
  891. Fire( bulletCount, barrelEnd, forward, pAttacker );
  892. m_fireLast = gpGlobals->curtime;
  893. }
  894. }
  895. else
  896. {
  897. m_fireLast = gpGlobals->curtime;
  898. }
  899. }
  900. //-----------------------------------------------------------------------------
  901. // Purpose: Fire targets and spawn sprites.
  902. // Input : bulletCount -
  903. // barrelEnd -
  904. // forward -
  905. // pAttacker -
  906. //-----------------------------------------------------------------------------
  907. void CFuncTank::Fire( int bulletCount, const Vector &barrelEnd, const Vector &forward, CBaseEntity *pAttacker )
  908. {
  909. if ( m_iszSpriteSmoke != NULL_STRING )
  910. {
  911. CSprite *pSprite = CSprite::SpriteCreate( STRING(m_iszSpriteSmoke), barrelEnd, TRUE );
  912. pSprite->AnimateAndDie( random->RandomFloat( 15.0, 20.0 ) );
  913. pSprite->SetTransparency( kRenderTransAlpha, m_clrRender->r, m_clrRender->g, m_clrRender->b, 255, kRenderFxNone );
  914. Vector vecVelocity( 0, 0, random->RandomFloat(40, 80) );
  915. pSprite->SetAbsVelocity( vecVelocity );
  916. pSprite->SetScale( m_spriteScale );
  917. }
  918. if ( m_iszSpriteFlash != NULL_STRING )
  919. {
  920. CSprite *pSprite = CSprite::SpriteCreate( STRING(m_iszSpriteFlash), barrelEnd, TRUE );
  921. pSprite->AnimateAndDie( 60 );
  922. pSprite->SetTransparency( kRenderTransAdd, 255, 255, 255, 255, kRenderFxNoDissipation );
  923. pSprite->SetScale( m_spriteScale );
  924. }
  925. m_OnFire.FireOutput(this, this);
  926. }
  927. void CFuncTank::TankTrace( const Vector &vecStart, const Vector &vecForward, const Vector &vecSpread, trace_t &tr )
  928. {
  929. Vector forward, right, up;
  930. AngleVectors( GetAbsAngles(), &forward, &right, &up );
  931. // get circular gaussian spread
  932. float x, y, z;
  933. do {
  934. x = random->RandomFloat(-0.5,0.5) + random->RandomFloat(-0.5,0.5);
  935. y = random->RandomFloat(-0.5,0.5) + random->RandomFloat(-0.5,0.5);
  936. z = x*x+y*y;
  937. } while (z > 1);
  938. Vector vecDir = vecForward +
  939. x * vecSpread.x * right +
  940. y * vecSpread.y * up;
  941. Vector vecEnd;
  942. vecEnd = vecStart + vecDir * MAX_TRACE_LENGTH;
  943. UTIL_TraceLine( vecStart, vecEnd, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );
  944. }
  945. void CFuncTank::StartRotSound( void )
  946. {
  947. if ( m_spawnflags & SF_TANK_SOUNDON )
  948. return;
  949. m_spawnflags |= SF_TANK_SOUNDON;
  950. if ( m_soundLoopRotate != NULL_STRING )
  951. {
  952. CPASAttenuationFilter filter( this );
  953. filter.MakeReliable();
  954. EmitSound_t ep;
  955. ep.m_nChannel = CHAN_STATIC;
  956. ep.m_pSoundName = (char*)STRING(m_soundLoopRotate);
  957. ep.m_flVolume = 0.85f;
  958. ep.m_SoundLevel = SNDLVL_NORM;
  959. EmitSound( filter, entindex(), ep );
  960. }
  961. if ( m_soundStartRotate != NULL_STRING )
  962. {
  963. CPASAttenuationFilter filter( this );
  964. EmitSound_t ep;
  965. ep.m_nChannel = CHAN_BODY;
  966. ep.m_pSoundName = (char*)STRING(m_soundStartRotate);
  967. ep.m_flVolume = 1.0f;
  968. ep.m_SoundLevel = SNDLVL_NORM;
  969. EmitSound( filter, entindex(), ep );
  970. }
  971. }
  972. void CFuncTank::StopRotSound( void )
  973. {
  974. if ( m_spawnflags & SF_TANK_SOUNDON )
  975. {
  976. if ( m_soundLoopRotate != NULL_STRING )
  977. {
  978. StopSound( entindex(), CHAN_STATIC, (char*)STRING(m_soundLoopRotate) );
  979. }
  980. if ( m_soundStopRotate != NULL_STRING )
  981. {
  982. CPASAttenuationFilter filter( this );
  983. EmitSound_t ep;
  984. ep.m_nChannel = CHAN_BODY;
  985. ep.m_pSoundName = (char*)STRING(m_soundStopRotate);
  986. ep.m_flVolume = 1.0f;
  987. ep.m_SoundLevel = SNDLVL_NORM;
  988. EmitSound( filter, entindex(), ep );
  989. }
  990. }
  991. m_spawnflags &= ~SF_TANK_SOUNDON;
  992. }
  993. // #############################################################################
  994. // CFuncTankGun
  995. // #############################################################################
  996. class CFuncTankGun : public CFuncTank
  997. {
  998. public:
  999. DECLARE_CLASS( CFuncTankGun, CFuncTank );
  1000. void Fire( int bulletCount, const Vector &barrelEnd, const Vector &forward, CBaseEntity *pAttacker );
  1001. };
  1002. LINK_ENTITY_TO_CLASS( func_tank, CFuncTankGun );
  1003. void CFuncTankGun::Fire( int bulletCount, const Vector &barrelEnd, const Vector &forward, CBaseEntity *pAttacker )
  1004. {
  1005. int i;
  1006. for ( i = 0; i < bulletCount; i++ )
  1007. {
  1008. switch( m_bulletType )
  1009. {
  1010. case TANK_BULLET_SMALL:
  1011. FireBullets( 1, barrelEnd, forward, gTankSpread[m_spread], MAX_TRACE_LENGTH, m_iSmallAmmoType, 1, -1, -1, m_iBulletDamage, pAttacker );
  1012. break;
  1013. case TANK_BULLET_MEDIUM:
  1014. FireBullets( 1, barrelEnd, forward, gTankSpread[m_spread], MAX_TRACE_LENGTH, m_iMediumAmmoType, 1, -1, -1, m_iBulletDamage, pAttacker );
  1015. break;
  1016. case TANK_BULLET_LARGE:
  1017. FireBullets( 1, barrelEnd, forward, gTankSpread[m_spread], MAX_TRACE_LENGTH, m_iLargeAmmoType, 1, -1, -1, m_iBulletDamage, pAttacker );
  1018. break;
  1019. default:
  1020. case TANK_BULLET_NONE:
  1021. break;
  1022. }
  1023. }
  1024. CFuncTank::Fire( bulletCount, barrelEnd, forward, pAttacker );
  1025. }
  1026. // #############################################################################
  1027. // CFuncTankPulseLaser
  1028. // #############################################################################
  1029. class CFuncTankPulseLaser : public CFuncTankGun
  1030. {
  1031. public:
  1032. DECLARE_CLASS( CFuncTankPulseLaser, CFuncTankGun );
  1033. DECLARE_DATADESC();
  1034. void Precache();
  1035. void Fire( int bulletCount, const Vector &barrelEnd, const Vector &forward, CBaseEntity *pAttacker );
  1036. float m_flPulseSpeed;
  1037. float m_flPulseWidth;
  1038. color32 m_flPulseColor;
  1039. float m_flPulseLife;
  1040. float m_flPulseLag;
  1041. string_t m_sPulseFireSound;
  1042. };
  1043. LINK_ENTITY_TO_CLASS( func_tankpulselaser, CFuncTankPulseLaser );
  1044. BEGIN_DATADESC( CFuncTankPulseLaser )
  1045. DEFINE_KEYFIELD( m_flPulseSpeed, FIELD_FLOAT, "PulseSpeed" ),
  1046. DEFINE_KEYFIELD( m_flPulseWidth, FIELD_FLOAT, "PulseWidth" ),
  1047. DEFINE_KEYFIELD( m_flPulseColor, FIELD_COLOR32, "PulseColor" ),
  1048. DEFINE_KEYFIELD( m_flPulseLife, FIELD_FLOAT, "PulseLife" ),
  1049. DEFINE_KEYFIELD( m_flPulseLag, FIELD_FLOAT, "PulseLag" ),
  1050. DEFINE_KEYFIELD( m_sPulseFireSound, FIELD_SOUNDNAME, "PulseFireSound" ),
  1051. END_DATADESC()
  1052. //------------------------------------------------------------------------------
  1053. // Purpose :
  1054. // Input :
  1055. // Output :
  1056. //------------------------------------------------------------------------------
  1057. void CFuncTankPulseLaser::Precache(void)
  1058. {
  1059. UTIL_PrecacheOther( "grenade_beam" );
  1060. if ( m_sPulseFireSound != NULL_STRING )
  1061. {
  1062. PrecacheScriptSound( STRING(m_sPulseFireSound) );
  1063. }
  1064. BaseClass::Precache();
  1065. }
  1066. //------------------------------------------------------------------------------
  1067. // Purpose :
  1068. // Input :
  1069. // Output :
  1070. //------------------------------------------------------------------------------
  1071. void CFuncTankPulseLaser::Fire( int bulletCount, const Vector &barrelEnd, const Vector &vecForward, CBaseEntity *pAttacker )
  1072. {
  1073. // --------------------------------------------------
  1074. // Get direction vectors for spread
  1075. // --------------------------------------------------
  1076. Vector vecUp = Vector(0,0,1);
  1077. Vector vecRight;
  1078. CrossProduct ( vecForward, vecUp, vecRight );
  1079. CrossProduct ( vecForward, -vecRight, vecUp );
  1080. for ( int i = 0; i < bulletCount; i++ )
  1081. {
  1082. // get circular gaussian spread
  1083. float x, y, z;
  1084. do {
  1085. x = random->RandomFloat(-0.5,0.5) + random->RandomFloat(-0.5,0.5);
  1086. y = random->RandomFloat(-0.5,0.5) + random->RandomFloat(-0.5,0.5);
  1087. z = x*x+y*y;
  1088. } while (z > 1);
  1089. Vector vecDir = vecForward + x * gTankSpread[m_spread].x * vecRight + y * gTankSpread[m_spread].y * vecUp;
  1090. CGrenadeBeam *pPulse = CGrenadeBeam::Create( pAttacker, barrelEnd);
  1091. pPulse->Format(m_flPulseColor, m_flPulseWidth);
  1092. pPulse->Shoot(vecDir,m_flPulseSpeed,m_flPulseLife,m_flPulseLag,m_iBulletDamage);
  1093. if ( m_sPulseFireSound != NULL_STRING )
  1094. {
  1095. CPASAttenuationFilter filter( this, 0.6f );
  1096. EmitSound_t ep;
  1097. ep.m_nChannel = CHAN_WEAPON;
  1098. ep.m_pSoundName = (char*)STRING(m_sPulseFireSound);
  1099. ep.m_flVolume = 1.0f;
  1100. ep.m_SoundLevel = ATTN_TO_SNDLVL( 0.6 );
  1101. EmitSound( filter, entindex(), ep );
  1102. }
  1103. }
  1104. CFuncTank::Fire( bulletCount, barrelEnd, vecForward, pAttacker );
  1105. }
  1106. // #############################################################################
  1107. // CFuncTankLaser
  1108. // #############################################################################
  1109. class CFuncTankLaser : public CFuncTank
  1110. {
  1111. DECLARE_CLASS( CFuncTankLaser, CFuncTank );
  1112. public:
  1113. void Activate( void );
  1114. void Fire( int bulletCount, const Vector &barrelEnd, const Vector &forward, CBaseEntity *pAttacker );
  1115. void Think( void );
  1116. CEnvLaser *GetLaser( void );
  1117. void UpdateOnRemove( void );
  1118. DECLARE_DATADESC();
  1119. private:
  1120. CEnvLaser *m_pLaser;
  1121. float m_laserTime;
  1122. string_t m_iszLaserName;
  1123. };
  1124. LINK_ENTITY_TO_CLASS( func_tanklaser, CFuncTankLaser );
  1125. BEGIN_DATADESC( CFuncTankLaser )
  1126. DEFINE_KEYFIELD( m_iszLaserName, FIELD_STRING, "laserentity" ),
  1127. DEFINE_FIELD( m_pLaser, FIELD_CLASSPTR ),
  1128. DEFINE_FIELD( m_laserTime, FIELD_TIME ),
  1129. END_DATADESC()
  1130. void CFuncTankLaser::Activate( void )
  1131. {
  1132. BaseClass::Activate();
  1133. if ( !GetLaser() )
  1134. {
  1135. UTIL_Remove(this);
  1136. Warning( "Laser tank with no env_laser!\n" );
  1137. }
  1138. else
  1139. {
  1140. m_pLaser->TurnOff();
  1141. }
  1142. }
  1143. void CFuncTankLaser::UpdateOnRemove( void )
  1144. {
  1145. if( GetLaser() )
  1146. {
  1147. m_pLaser->TurnOff();
  1148. }
  1149. BaseClass::UpdateOnRemove();
  1150. }
  1151. CEnvLaser *CFuncTankLaser::GetLaser( void )
  1152. {
  1153. if ( m_pLaser )
  1154. return m_pLaser;
  1155. CBaseEntity *pLaser = gEntList.FindEntityByName( NULL, m_iszLaserName );
  1156. while ( pLaser )
  1157. {
  1158. // Found the landmark
  1159. if ( FClassnameIs( pLaser, "env_laser" ) )
  1160. {
  1161. m_pLaser = (CEnvLaser *)pLaser;
  1162. break;
  1163. }
  1164. else
  1165. {
  1166. pLaser = gEntList.FindEntityByName( pLaser, m_iszLaserName );
  1167. }
  1168. }
  1169. return m_pLaser;
  1170. }
  1171. void CFuncTankLaser::Think( void )
  1172. {
  1173. if ( m_pLaser && (gpGlobals->curtime > m_laserTime) )
  1174. m_pLaser->TurnOff();
  1175. CFuncTank::Think();
  1176. }
  1177. void CFuncTankLaser::Fire( int bulletCount, const Vector &barrelEnd, const Vector &forward, CBaseEntity *pAttacker )
  1178. {
  1179. int i;
  1180. trace_t tr;
  1181. if ( GetLaser() )
  1182. {
  1183. for ( i = 0; i < bulletCount; i++ )
  1184. {
  1185. m_pLaser->SetLocalOrigin( barrelEnd );
  1186. TankTrace( barrelEnd, forward, gTankSpread[m_spread], tr );
  1187. m_laserTime = gpGlobals->curtime;
  1188. m_pLaser->TurnOn();
  1189. m_pLaser->SetFireTime( gpGlobals->curtime - 1.0 );
  1190. m_pLaser->FireAtPoint( tr );
  1191. m_pLaser->SetNextThink( TICK_NEVER_THINK );
  1192. }
  1193. CFuncTank::Fire( bulletCount, barrelEnd, forward, this );
  1194. }
  1195. }
  1196. class CFuncTankRocket : public CFuncTank
  1197. {
  1198. public:
  1199. DECLARE_CLASS( CFuncTankRocket, CFuncTank );
  1200. void Precache( void );
  1201. void Fire( int bulletCount, const Vector &barrelEnd, const Vector &forward, CBaseEntity *pAttacker );
  1202. };
  1203. LINK_ENTITY_TO_CLASS( func_tankrocket, CFuncTankRocket );
  1204. void CFuncTankRocket::Precache( void )
  1205. {
  1206. UTIL_PrecacheOther( "rpg_rocket" );
  1207. CFuncTank::Precache();
  1208. }
  1209. void CFuncTankRocket::Fire( int bulletCount, const Vector &barrelEnd, const Vector &forward, CBaseEntity *pAttacker )
  1210. {
  1211. int i;
  1212. for ( i = 0; i < bulletCount; i++ )
  1213. {
  1214. CBaseEntity *pRocket = CBaseEntity::Create( "rpg_rocket", barrelEnd, GetAbsAngles(), this );
  1215. pRocket->SetAbsAngles( GetAbsAngles() );
  1216. }
  1217. CFuncTank::Fire( bulletCount, barrelEnd, forward, this );
  1218. }
  1219. class CFuncTankMortar : public CFuncTank
  1220. {
  1221. public:
  1222. DECLARE_CLASS( CFuncTankMortar, CFuncTank );
  1223. void Precache( void );
  1224. void FiringSequence( const Vector &barrelEnd, const Vector &forward, CBaseEntity *pAttacker );
  1225. void Fire( int bulletCount, const Vector &barrelEnd, const Vector &vecForward, CBaseEntity *pAttacker );
  1226. void ShootGun(void);
  1227. // Input handlers.
  1228. void InputShootGun( inputdata_t &inputdata );
  1229. DECLARE_DATADESC();
  1230. int m_Magnitude;
  1231. float m_fireDelay;
  1232. string_t m_fireStartSound;
  1233. string_t m_fireEndSound;
  1234. // store future firing event
  1235. CBaseEntity *m_pAttacker;
  1236. };
  1237. LINK_ENTITY_TO_CLASS( func_tankmortar, CFuncTankMortar );
  1238. BEGIN_DATADESC( CFuncTankMortar )
  1239. DEFINE_KEYFIELD( m_Magnitude, FIELD_INTEGER, "iMagnitude" ),
  1240. DEFINE_KEYFIELD( m_fireDelay, FIELD_FLOAT, "firedelay" ),
  1241. DEFINE_KEYFIELD( m_fireStartSound, FIELD_STRING, "firestartsound" ),
  1242. DEFINE_KEYFIELD( m_fireEndSound, FIELD_STRING, "fireendsound" ),
  1243. DEFINE_FIELD( m_pAttacker, FIELD_CLASSPTR ),
  1244. // Inputs
  1245. DEFINE_INPUTFUNC( FIELD_VOID, "ShootGun", InputShootGun ),
  1246. END_DATADESC()
  1247. void CFuncTankMortar::Precache( void )
  1248. {
  1249. if ( m_fireStartSound != NULL_STRING )
  1250. PrecacheScriptSound( STRING(m_fireStartSound) );
  1251. if ( m_fireEndSound != NULL_STRING )
  1252. PrecacheScriptSound( STRING(m_fireEndSound) );
  1253. BaseClass::Precache();
  1254. }
  1255. //-----------------------------------------------------------------------------
  1256. // Purpose: Input handler to make the tank shoot.
  1257. //-----------------------------------------------------------------------------
  1258. void CFuncTankMortar::InputShootGun( inputdata_t &inputdata )
  1259. {
  1260. ShootGun();
  1261. }
  1262. //-----------------------------------------------------------------------------
  1263. // Purpose:
  1264. //-----------------------------------------------------------------------------
  1265. void CFuncTankMortar::ShootGun( void )
  1266. {
  1267. Vector forward;
  1268. AngleVectors( GetLocalAngles(), &forward );
  1269. UpdateMatrix();
  1270. forward = m_parentMatrix.ApplyRotation( forward );
  1271. // use cached firing state
  1272. Fire( 1, WorldBarrelPosition(), forward, m_pAttacker );
  1273. }
  1274. void CFuncTankMortar::FiringSequence( const Vector &barrelEnd, const Vector &forward, CBaseEntity *pAttacker )
  1275. {
  1276. if ( m_fireLast != 0 )
  1277. {
  1278. int bulletCount = (gpGlobals->curtime - m_fireLast) * m_fireRate;
  1279. // Only create 1 explosion
  1280. if ( bulletCount > 0 )
  1281. {
  1282. // fire in "firedelay" seconds
  1283. if ( m_fireStartSound != NULL_STRING )
  1284. {
  1285. CPASAttenuationFilter filter( this );
  1286. EmitSound_t ep;
  1287. ep.m_nChannel = CHAN_WEAPON;
  1288. ep.m_pSoundName = (char*)STRING(m_fireStartSound);
  1289. ep.m_flVolume = 1.0f;
  1290. ep.m_SoundLevel = SNDLVL_NORM;
  1291. EmitSound( filter, entindex(), ep );
  1292. }
  1293. if ( m_fireDelay != 0 )
  1294. {
  1295. g_EventQueue.AddEvent( this, "ShootGun", m_fireDelay, pAttacker, this, 0 );
  1296. }
  1297. else
  1298. {
  1299. ShootGun();
  1300. }
  1301. m_fireLast = gpGlobals->curtime;
  1302. }
  1303. }
  1304. else
  1305. {
  1306. m_fireLast = gpGlobals->curtime;
  1307. }
  1308. }
  1309. void CFuncTankMortar::Fire( int bulletCount, const Vector &barrelEnd, const Vector &vecForward, CBaseEntity *pAttacker )
  1310. {
  1311. if ( m_fireEndSound != NULL_STRING )
  1312. {
  1313. CPASAttenuationFilter filter( this );
  1314. EmitSound_t ep;
  1315. ep.m_nChannel = CHAN_WEAPON;
  1316. ep.m_pSoundName = STRING(m_fireEndSound);
  1317. ep.m_flVolume = 1.0f;
  1318. ep.m_SoundLevel = SNDLVL_NORM;
  1319. EmitSound( filter, entindex(), ep );
  1320. }
  1321. trace_t tr;
  1322. TankTrace( barrelEnd, vecForward, gTankSpread[m_spread], tr );
  1323. const CBaseEntity * ent = NULL;
  1324. if ( g_pGameRules->IsMultiplayer() )
  1325. {
  1326. // temp remove suppress host
  1327. ent = te->GetSuppressHost();
  1328. te->SetSuppressHost( NULL );
  1329. }
  1330. ExplosionCreate( tr.endpos, GetAbsAngles(), this, m_Magnitude, 0, true );
  1331. if ( g_pGameRules->IsMultiplayer() )
  1332. {
  1333. te->SetSuppressHost( (CBaseEntity *) ent );
  1334. }
  1335. BaseClass::Fire( bulletCount, barrelEnd, vecForward, this );
  1336. }
  1337. //-----------------------------------------------------------------------------
  1338. // Purpose: Func tank that fires physics cannisters placed on it
  1339. //-----------------------------------------------------------------------------
  1340. class CFuncTankPhysCannister : public CFuncTank
  1341. {
  1342. public:
  1343. DECLARE_CLASS( CFuncTankPhysCannister, CFuncTank );
  1344. DECLARE_DATADESC();
  1345. void Activate( void );
  1346. void Fire( int bulletCount, const Vector &barrelEnd, const Vector &forward, CBaseEntity *pAttacker );
  1347. protected:
  1348. string_t m_iszBarrelVolume;
  1349. CHandle<CBaseTrigger> m_hBarrelVolume;
  1350. };
  1351. LINK_ENTITY_TO_CLASS( func_tankphyscannister, CFuncTankPhysCannister );
  1352. BEGIN_DATADESC( CFuncTankPhysCannister )
  1353. DEFINE_KEYFIELD( m_iszBarrelVolume, FIELD_STRING, "barrel_volume" ),
  1354. DEFINE_FIELD( m_hBarrelVolume, FIELD_EHANDLE ),
  1355. END_DATADESC()
  1356. //-----------------------------------------------------------------------------
  1357. // Purpose:
  1358. //-----------------------------------------------------------------------------
  1359. void CFuncTankPhysCannister::Activate( void )
  1360. {
  1361. BaseClass::Activate();
  1362. m_hBarrelVolume = NULL;
  1363. // Find our barrel volume
  1364. if ( m_iszBarrelVolume != NULL_STRING )
  1365. {
  1366. m_hBarrelVolume = dynamic_cast<CBaseTrigger*>( gEntList.FindEntityByName( NULL, m_iszBarrelVolume ) );
  1367. }
  1368. if ( !m_hBarrelVolume )
  1369. {
  1370. Msg("ERROR: Couldn't find barrel volume for func_tankphyscannister %s.\n", STRING(GetEntityName()) );
  1371. UTIL_Remove( this );
  1372. }
  1373. }
  1374. //-----------------------------------------------------------------------------
  1375. // Purpose:
  1376. //-----------------------------------------------------------------------------
  1377. void CFuncTankPhysCannister::Fire( int bulletCount, const Vector &barrelEnd, const Vector &forward, CBaseEntity *pAttacker )
  1378. {
  1379. Assert( m_hBarrelVolume );
  1380. // Do we have a cannister in our barrel volume?
  1381. CPhysicsCannister *pCannister = (CPhysicsCannister *)m_hBarrelVolume->GetTouchedEntityOfType( "physics_cannister" );
  1382. if ( !pCannister )
  1383. {
  1384. // Play a no-ammo sound
  1385. return;
  1386. }
  1387. // Fire the cannister!
  1388. pCannister->CannisterFire( pAttacker );
  1389. }