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.

1654 lines
39 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "ai_basenpc.h"
  8. #include "trains.h"
  9. #include "ndebugoverlay.h"
  10. #include "entitylist.h"
  11. #include "engine/IEngineSound.h"
  12. #include "hl1_ents.h"
  13. #include "doors.h"
  14. #include "soundent.h"
  15. #include "hl1_basegrenade.h"
  16. #include "shake.h"
  17. #include "globalstate.h"
  18. #include "soundscape.h"
  19. #include "buttons.h"
  20. #include "Sprite.h"
  21. #include "actanimating.h"
  22. #include "npcevent.h"
  23. #include "func_break.h"
  24. #include "hl1_shareddefs.h"
  25. #include "eventqueue.h"
  26. //
  27. // TRIGGERS: trigger_auto, trigger_relay, multimanager: replaced in src by logic_auto and logic_relay
  28. //
  29. // This trigger will fire when the level spawns (or respawns if not fire once)
  30. // It will check a global state before firing.
  31. #define SF_AUTO_FIREONCE 0x0001
  32. class CAutoTrigger : public CBaseEntity
  33. {
  34. DECLARE_CLASS( CAutoTrigger, CBaseEntity );
  35. public:
  36. void Spawn( void );
  37. void Precache( void );
  38. void Think( void );
  39. int ObjectCaps( void ) { return BaseClass::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
  40. DECLARE_DATADESC();
  41. private:
  42. COutputEvent m_OnTrigger;
  43. string_t m_globalstate;
  44. };
  45. LINK_ENTITY_TO_CLASS( trigger_auto, CAutoTrigger );
  46. BEGIN_DATADESC( CAutoTrigger )
  47. DEFINE_KEYFIELD( m_globalstate, FIELD_STRING, "globalstate" ),
  48. // Outputs
  49. DEFINE_OUTPUT(m_OnTrigger, "OnTrigger"),
  50. END_DATADESC()
  51. void CAutoTrigger::Spawn( void )
  52. {
  53. Precache();
  54. }
  55. void CAutoTrigger::Precache( void )
  56. {
  57. SetNextThink( gpGlobals->curtime + 0.1f );
  58. }
  59. //-----------------------------------------------------------------------------
  60. // Purpose: Checks the global state and fires targets if the global state is set.
  61. //-----------------------------------------------------------------------------
  62. void CAutoTrigger::Think( void )
  63. {
  64. if ( !m_globalstate || GlobalEntity_GetState( m_globalstate ) == GLOBAL_ON )
  65. {
  66. m_OnTrigger.FireOutput(NULL, this);
  67. if ( m_spawnflags & SF_AUTO_FIREONCE )
  68. UTIL_Remove( this );
  69. }
  70. }
  71. #define SF_RELAY_FIREONCE 0x0001
  72. class CTriggerRelay : public CBaseEntity
  73. {
  74. DECLARE_CLASS( CTriggerRelay, CBaseEntity );
  75. public:
  76. CTriggerRelay( void );
  77. bool KeyValue( const char *szKeyName, const char *szValue );
  78. void Spawn( void );
  79. void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
  80. void RefireThink( void );
  81. int ObjectCaps( void ) { return BaseClass::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
  82. DECLARE_DATADESC();
  83. private:
  84. USE_TYPE triggerType;
  85. float m_flRefireInterval;
  86. float m_flRefireDuration;
  87. float m_flTimeRefireDone;
  88. USE_TYPE m_TargetUseType;
  89. float m_flTargetValue;
  90. COutputEvent m_OnTrigger;
  91. };
  92. LINK_ENTITY_TO_CLASS( trigger_relay, CTriggerRelay );
  93. BEGIN_DATADESC( CTriggerRelay )
  94. DEFINE_FIELD( triggerType, FIELD_INTEGER ),
  95. DEFINE_KEYFIELD( m_flRefireInterval, FIELD_FLOAT, "repeatinterval" ),
  96. DEFINE_KEYFIELD( m_flRefireDuration, FIELD_FLOAT, "repeatduration" ),
  97. DEFINE_FIELD( m_flTimeRefireDone, FIELD_TIME ),
  98. DEFINE_FIELD( m_TargetUseType, FIELD_INTEGER ),
  99. DEFINE_FIELD( m_flTargetValue, FIELD_FLOAT ),
  100. // Function Pointers
  101. DEFINE_FUNCTION( RefireThink ),
  102. // Outputs
  103. DEFINE_OUTPUT(m_OnTrigger, "OnTrigger"),
  104. END_DATADESC()
  105. CTriggerRelay::CTriggerRelay( void )
  106. {
  107. m_flRefireInterval = -1;
  108. m_flRefireDuration = -1;
  109. m_flTimeRefireDone = -1;
  110. }
  111. bool CTriggerRelay::KeyValue( const char *szKeyName, const char *szValue )
  112. {
  113. if (FStrEq(szKeyName, "triggerstate"))
  114. {
  115. int type = atoi( szValue );
  116. switch( type )
  117. {
  118. case 0:
  119. triggerType = USE_OFF;
  120. break;
  121. case 2:
  122. triggerType = USE_TOGGLE;
  123. break;
  124. default:
  125. triggerType = USE_ON;
  126. break;
  127. }
  128. }
  129. else
  130. {
  131. return BaseClass::KeyValue( szKeyName, szValue );
  132. }
  133. return true;
  134. }
  135. void CTriggerRelay::Spawn( void )
  136. {
  137. }
  138. void CTriggerRelay::RefireThink( void )
  139. {
  140. // sending this as Activator and Caller right now. Seems the safest thing
  141. // since whatever fired the relay the first time may no longer exist.
  142. Use( this, this, m_TargetUseType, m_flTargetValue );
  143. if( gpGlobals->curtime > m_flTimeRefireDone )
  144. {
  145. UTIL_Remove( this );
  146. }
  147. else
  148. {
  149. SetNextThink( gpGlobals->curtime + m_flRefireInterval );
  150. }
  151. }
  152. void CTriggerRelay::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
  153. {
  154. m_OnTrigger.FireOutput(pActivator, this);
  155. if ( m_spawnflags & SF_RELAY_FIREONCE )
  156. {
  157. UTIL_Remove( this );
  158. }
  159. else if( m_flRefireDuration != -1 && m_flTimeRefireDone == -1 )
  160. {
  161. // Set up to refire this target automatically
  162. m_TargetUseType = useType;
  163. m_flTargetValue = value;
  164. m_flTimeRefireDone = gpGlobals->curtime + m_flRefireDuration;
  165. SetThink( &CTriggerRelay::RefireThink );
  166. SetNextThink( gpGlobals->curtime + m_flRefireInterval );
  167. }
  168. }
  169. // The Multimanager Entity - when fired, will fire up to 16 targets
  170. // at specified times.
  171. class CMultiManager : public CPointEntity
  172. {
  173. DECLARE_CLASS( CMultiManager, CPointEntity );
  174. public:
  175. bool KeyValue( const char *szKeyName, const char *szValue );
  176. void Spawn ( void );
  177. void ManagerThink ( void );
  178. void ManagerUse ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
  179. #if _DEBUG
  180. void ManagerReport( void );
  181. #endif
  182. bool HasTarget( string_t targetname );
  183. int ObjectCaps( void ) { return BaseClass::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
  184. DECLARE_DATADESC();
  185. int m_cTargets; // the total number of targets in this manager's fire list.
  186. int m_index; // Current target
  187. float m_flWait;
  188. EHANDLE m_hActivator;
  189. float m_startTime;// Time we started firing
  190. string_t m_iTargetName [ MAX_MULTI_TARGETS ];// list if indexes into global string array
  191. float m_flTargetDelay [ MAX_MULTI_TARGETS ];// delay (in seconds) from time of manager fire to target fire
  192. COutputEvent m_OnTrigger;
  193. void InputManagerTrigger( inputdata_t &data );
  194. };
  195. LINK_ENTITY_TO_CLASS( multi_manager, CMultiManager );
  196. // Global Savedata for multi_manager
  197. BEGIN_DATADESC( CMultiManager )
  198. DEFINE_KEYFIELD( m_flWait, FIELD_FLOAT, "wait" ),
  199. DEFINE_FIELD( m_cTargets, FIELD_INTEGER ),
  200. DEFINE_FIELD( m_index, FIELD_INTEGER ),
  201. DEFINE_FIELD( m_hActivator, FIELD_EHANDLE ),
  202. DEFINE_FIELD( m_startTime, FIELD_TIME ),
  203. DEFINE_ARRAY( m_iTargetName, FIELD_STRING, MAX_MULTI_TARGETS ),
  204. DEFINE_ARRAY( m_flTargetDelay, FIELD_FLOAT, MAX_MULTI_TARGETS ),
  205. // Function Pointers
  206. DEFINE_FUNCTION( ManagerThink ),
  207. DEFINE_FUNCTION( ManagerUse ),
  208. #if _DEBUG
  209. DEFINE_FUNCTION( ManagerReport ),
  210. #endif
  211. // Outputs
  212. DEFINE_OUTPUT(m_OnTrigger, "OnTrigger"),
  213. DEFINE_INPUTFUNC( FIELD_VOID, "Trigger", InputManagerTrigger ),
  214. END_DATADESC()
  215. void CMultiManager::InputManagerTrigger( inputdata_t &data )
  216. {
  217. ManagerUse ( NULL, NULL, USE_TOGGLE, 0 );
  218. }
  219. bool CMultiManager::KeyValue( const char *szKeyName, const char *szValue )
  220. {
  221. if ( BaseClass::KeyValue( szKeyName, szValue ) )
  222. {
  223. return true;
  224. }
  225. else // add this field to the target list
  226. {
  227. // this assumes that additional fields are targetnames and their values are delay values.
  228. if ( m_cTargets < MAX_MULTI_TARGETS )
  229. {
  230. char tmp[128];
  231. UTIL_StripToken( szKeyName, tmp, Q_ARRAYSIZE( tmp ) );
  232. m_iTargetName [ m_cTargets ] = AllocPooledString( tmp );
  233. m_flTargetDelay [ m_cTargets ] = atof (szValue);
  234. m_cTargets++;
  235. }
  236. else
  237. {
  238. return false;
  239. }
  240. }
  241. return true;
  242. }
  243. void CMultiManager::Spawn( void )
  244. {
  245. SetSolid( SOLID_NONE );
  246. SetUse ( &CMultiManager::ManagerUse );
  247. SetThink ( &CMultiManager::ManagerThink);
  248. // Sort targets
  249. // Quick and dirty bubble sort
  250. int swapped = 1;
  251. while ( swapped )
  252. {
  253. swapped = 0;
  254. for ( int i = 1; i < m_cTargets; i++ )
  255. {
  256. if ( m_flTargetDelay[i] < m_flTargetDelay[i-1] )
  257. {
  258. // Swap out of order elements
  259. string_t name = m_iTargetName[i];
  260. float delay = m_flTargetDelay[i];
  261. m_iTargetName[i] = m_iTargetName[i-1];
  262. m_flTargetDelay[i] = m_flTargetDelay[i-1];
  263. m_iTargetName[i-1] = name;
  264. m_flTargetDelay[i-1] = delay;
  265. swapped = 1;
  266. }
  267. }
  268. }
  269. }
  270. bool CMultiManager::HasTarget( string_t targetname )
  271. {
  272. for ( int i = 0; i < m_cTargets; i++ )
  273. if ( FStrEq(STRING(targetname), STRING(m_iTargetName[i])) )
  274. return true;
  275. return false;
  276. }
  277. // Designers were using this to fire targets that may or may not exist --
  278. // so I changed it to use the standard target fire code, made it a little simpler.
  279. void CMultiManager::ManagerThink ( void )
  280. {
  281. float t;
  282. t = gpGlobals->curtime - m_startTime;
  283. while ( m_index < m_cTargets && m_flTargetDelay[ m_index ] <= t )
  284. {
  285. FireTargets( STRING( m_iTargetName[ m_index ] ), m_hActivator, this, USE_TOGGLE, 0 );
  286. m_index++;
  287. }
  288. if ( m_index >= m_cTargets )// have we fired all targets?
  289. {
  290. SetThink( NULL );
  291. SetUse ( &CMultiManager::ManagerUse );// allow manager re-use
  292. }
  293. else
  294. {
  295. SetNextThink( m_startTime + m_flTargetDelay[ m_index ] );
  296. }
  297. }
  298. // The USE function builds the time table and starts the entity thinking.
  299. void CMultiManager::ManagerUse ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
  300. {
  301. m_hActivator = pActivator;
  302. m_index = 0;
  303. m_startTime = gpGlobals->curtime;
  304. m_OnTrigger.FireOutput(pActivator, this);
  305. // Calculate the time to re-enable the multimanager - just after the last output is fired.
  306. // dvsents2: need to disable multimanager until last output is fired
  307. //m_fEnableTime = gpGlobals->curtime + m_OnTrigger.GetMaxDelay();
  308. SetUse( NULL );// disable use until all targets have fired
  309. SetThink ( &CMultiManager::ManagerThink );
  310. SetNextThink( gpGlobals->curtime );
  311. }
  312. #if _DEBUG
  313. void CMultiManager::ManagerReport ( void )
  314. {
  315. int cIndex;
  316. for ( cIndex = 0 ; cIndex < m_cTargets ; cIndex++ )
  317. {
  318. Msg( "%s %f\n", STRING(m_iTargetName[cIndex]), m_flTargetDelay[cIndex] );
  319. }
  320. }
  321. #endif
  322. //
  323. // Pendulum
  324. //
  325. #define SF_PENDULUM_SWING 2
  326. LINK_ENTITY_TO_CLASS( func_pendulum, CPendulum );
  327. BEGIN_DATADESC( CPendulum )
  328. DEFINE_FIELD( m_flAccel, FIELD_FLOAT ),
  329. DEFINE_FIELD( m_flTime, FIELD_TIME ),
  330. DEFINE_FIELD( m_flMaxSpeed, FIELD_FLOAT ),
  331. DEFINE_FIELD( m_flDampSpeed, FIELD_FLOAT ),
  332. DEFINE_FIELD( m_vCenter, FIELD_VECTOR ),
  333. DEFINE_FIELD( m_vStart, FIELD_VECTOR ),
  334. DEFINE_KEYFIELD( m_flMoveDistance, FIELD_FLOAT, "pendistance" ),
  335. DEFINE_KEYFIELD( m_flDamp, FIELD_FLOAT, "damp" ),
  336. DEFINE_KEYFIELD( m_flBlockDamage, FIELD_FLOAT, "dmg" ),
  337. DEFINE_FUNCTION( PendulumUse ),
  338. DEFINE_FUNCTION( Swing ),
  339. DEFINE_FUNCTION( Stop ),
  340. DEFINE_FUNCTION( RopeTouch ),
  341. DEFINE_FIELD( m_hEnemy, FIELD_EHANDLE ),
  342. DEFINE_INPUTFUNC( FIELD_VOID, "Activate", InputActivate ),
  343. END_DATADESC()
  344. void CPendulum::Spawn( void )
  345. {
  346. CBaseToggle::AxisDir();
  347. m_flDamp *= 0.001;
  348. if ( FBitSet ( m_spawnflags, SF_DOOR_PASSABLE ) )
  349. SetSolid( SOLID_NONE );
  350. else
  351. SetSolid( SOLID_BBOX );
  352. SetMoveType( MOVETYPE_PUSH );
  353. SetModel( STRING(GetModelName()) );
  354. if ( m_flMoveDistance != 0 )
  355. {
  356. if ( m_flSpeed == 0 )
  357. m_flSpeed = 100;
  358. m_flAccel = ( m_flSpeed * m_flSpeed ) / ( 2 * fabs( m_flMoveDistance )); // Calculate constant acceleration from speed and distance
  359. m_flMaxSpeed = m_flSpeed;
  360. m_vStart = GetAbsAngles();
  361. m_vCenter = GetAbsAngles() + ( m_flMoveDistance * 0.05 ) * m_vecMoveAng;
  362. if ( FBitSet( m_spawnflags, SF_BRUSH_ROTATE_START_ON ) )
  363. {
  364. SetThink( &CBaseEntity::SUB_CallUseToggle );
  365. SetNextThink( gpGlobals->curtime + 0.1f );
  366. }
  367. m_flSpeed = 0;
  368. SetUse( &CPendulum::PendulumUse );
  369. VPhysicsInitShadow( false, false );
  370. ///VPhysicsGetObject()->SetPosition( GetAbsOrigin(), pev->absangles );
  371. }
  372. if ( FBitSet( m_spawnflags, SF_PENDULUM_SWING ) )
  373. {
  374. SetTouch ( &CPendulum::RopeTouch );
  375. }
  376. }
  377. void CPendulum::PendulumUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
  378. {
  379. if ( m_flSpeed ) // Pendulum is moving, stop it and auto-return if necessary
  380. {
  381. if ( FBitSet( m_spawnflags, SF_BRUSH_ROTATE_START_ON ) )
  382. {
  383. float delta;
  384. delta = CBaseToggle::AxisDelta( m_spawnflags, GetAbsAngles(), m_vStart );
  385. SetLocalAngularVelocity( m_flMaxSpeed * m_vecMoveAng );
  386. SetNextThink( gpGlobals->curtime + delta / m_flMaxSpeed);
  387. SetThink( &CPendulum::Stop );
  388. }
  389. else
  390. {
  391. m_flSpeed = 0; // Dead stop
  392. SetThink( NULL );
  393. SetLocalAngularVelocity( QAngle( 0, 0, 0 ) );
  394. }
  395. }
  396. else
  397. {
  398. SetNextThink( gpGlobals->curtime + 0.1f ); // Start the pendulum moving
  399. m_flTime = gpGlobals->curtime; // Save time to calculate dt
  400. SetThink( &CPendulum::Swing );
  401. m_flDampSpeed = m_flMaxSpeed;
  402. }
  403. }
  404. void CPendulum::InputActivate( inputdata_t &inputdata )
  405. {
  406. SetNextThink( gpGlobals->curtime + 0.1f ); // Start the pendulum moving
  407. m_flTime = gpGlobals->curtime; // Save time to calculate dt
  408. SetThink( &CPendulum::Swing );
  409. m_flDampSpeed = m_flMaxSpeed;
  410. }
  411. void CPendulum::Stop( void )
  412. {
  413. SetAbsAngles( m_vStart );
  414. m_flSpeed = 0;
  415. SetThink( NULL );
  416. SetLocalAngularVelocity( QAngle ( 0, 0, 0 ) );
  417. }
  418. void CPendulum::Blocked( CBaseEntity *pOther )
  419. {
  420. m_flTime = gpGlobals->curtime;
  421. }
  422. void CPendulum::Swing( void )
  423. {
  424. float delta, dt;
  425. delta = CBaseToggle::AxisDelta( m_spawnflags, GetAbsAngles(), m_vCenter );
  426. dt = gpGlobals->curtime - m_flTime; // How much time has passed?
  427. m_flTime = gpGlobals->curtime; // Remember the last time called
  428. if ( delta > 0 && m_flAccel > 0 )
  429. m_flSpeed -= m_flAccel * dt; // Integrate velocity
  430. else
  431. m_flSpeed += m_flAccel * dt;
  432. if ( m_flSpeed > m_flMaxSpeed )
  433. m_flSpeed = m_flMaxSpeed;
  434. else if ( m_flSpeed < -m_flMaxSpeed )
  435. m_flSpeed = -m_flMaxSpeed;
  436. // scale the destdelta vector by the time spent traveling to get velocity
  437. SetLocalAngularVelocity( m_flSpeed * m_vecMoveAng );
  438. // Call this again
  439. SetNextThink( gpGlobals->curtime + 0.1f );
  440. SetMoveDoneTime( 0.1 );
  441. if ( m_flDamp )
  442. {
  443. m_flDampSpeed -= m_flDamp * m_flDampSpeed * dt;
  444. if ( m_flDampSpeed < 30.0 )
  445. {
  446. SetAbsAngles( m_vCenter );
  447. m_flSpeed = 0;
  448. SetThink( NULL );
  449. SetLocalAngularVelocity( QAngle( 0, 0, 0 ) );
  450. }
  451. else if ( m_flSpeed > m_flDampSpeed )
  452. m_flSpeed = m_flDampSpeed;
  453. else if ( m_flSpeed < -m_flDampSpeed )
  454. m_flSpeed = -m_flDampSpeed;
  455. }
  456. }
  457. void CPendulum::Touch ( CBaseEntity *pOther )
  458. {
  459. if ( m_flBlockDamage <= 0 )
  460. return;
  461. // we can't hurt this thing, so we're not concerned with it
  462. if ( !pOther->m_takedamage )
  463. return;
  464. // calculate damage based on rotation speed
  465. float damage = m_flBlockDamage * m_flSpeed * 0.01;
  466. if ( damage < 0 )
  467. damage = -damage;
  468. pOther->TakeDamage( CTakeDamageInfo( this, this, damage, DMG_CRUSH ) );
  469. Vector vNewVel = (pOther->GetAbsOrigin() - GetAbsOrigin());
  470. VectorNormalize( vNewVel );
  471. pOther->SetAbsVelocity( vNewVel * damage );
  472. }
  473. void CPendulum::RopeTouch ( CBaseEntity *pOther )
  474. {
  475. if ( !pOther->IsPlayer() )
  476. {// not a player!
  477. DevMsg ( 2, "Not a client\n" );
  478. return;
  479. }
  480. if ( pOther == GetEnemy() )
  481. return;
  482. m_hEnemy = pOther;
  483. pOther->SetAbsVelocity( Vector ( 0, 0, 0 ) );
  484. pOther->SetMoveType( MOVETYPE_NONE );
  485. }
  486. //
  487. // MORTARS
  488. //
  489. class CFuncMortarField : public CBaseToggle
  490. {
  491. DECLARE_CLASS( CFuncMortarField, CBaseToggle );
  492. public:
  493. void Spawn( void );
  494. void Precache( void );
  495. void KeyValue( KeyValueData *pkvd );
  496. // Bmodels don't go across transitions
  497. virtual int ObjectCaps( void ) { return CBaseToggle::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
  498. /* virtual int Save( CSave &save );
  499. virtual int Restore( CRestore &restore );
  500. static TYPEDESCRIPTION m_SaveData[];*/
  501. //void EXPORT FieldUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
  502. void InputTrigger ( inputdata_t &inputdata );
  503. DECLARE_DATADESC();
  504. string_t m_iszXController;
  505. string_t m_iszYController;
  506. float m_flSpread;
  507. int m_iCount;
  508. int m_fControl;
  509. };
  510. LINK_ENTITY_TO_CLASS( func_mortar_field, CFuncMortarField );
  511. BEGIN_DATADESC( CFuncMortarField )
  512. DEFINE_KEYFIELD( m_iszXController, FIELD_STRING, "m_iszXController" ),
  513. DEFINE_KEYFIELD( m_iszYController, FIELD_STRING, "m_iszYController" ),
  514. DEFINE_KEYFIELD( m_flSpread, FIELD_FLOAT, "m_flSpread" ),
  515. DEFINE_KEYFIELD( m_iCount, FIELD_INTEGER, "m_iCount" ),
  516. DEFINE_KEYFIELD( m_fControl, FIELD_INTEGER, "m_fControl" ),
  517. DEFINE_INPUTFUNC( FIELD_VOID, "Trigger", InputTrigger ),
  518. END_DATADESC()
  519. // Drop bombs from above
  520. void CFuncMortarField::Spawn( void )
  521. {
  522. SetSolid( SOLID_NONE );
  523. SetModel( STRING(GetModelName()) ); // set size and link into world
  524. SetMoveType( MOVETYPE_NONE );
  525. AddEffects( EF_NODRAW );
  526. // SetUse( FieldUse );
  527. Precache();
  528. }
  529. void CFuncMortarField::Precache( void )
  530. {
  531. PrecacheModel( "sprites/lgtning.vmt" );
  532. PrecacheScriptSound( "MortarField.Trigger" );
  533. }
  534. void CFuncMortarField::InputTrigger( inputdata_t &inputdata )
  535. {
  536. Vector vecStart;
  537. CollisionProp()->RandomPointInBounds( Vector( 0, 0, 1 ), Vector( 1, 1, 1 ), &vecStart );
  538. switch( m_fControl )
  539. {
  540. case 0: // random
  541. break;
  542. case 1: // Trigger Activator
  543. if (inputdata.pActivator != NULL)
  544. {
  545. vecStart.x = inputdata.pActivator->GetAbsOrigin().x;
  546. vecStart.y = inputdata.pActivator->GetAbsOrigin().y;
  547. }
  548. break;
  549. case 2: // table
  550. {
  551. CBaseEntity *pController;
  552. if ( m_iszXController != NULL_STRING )
  553. {
  554. pController = gEntList.FindEntityByName( NULL, STRING(m_iszXController) );
  555. if (pController != NULL)
  556. {
  557. if ( FClassnameIs( pController, "momentary_rot_button" ) )
  558. {
  559. CMomentaryRotButton *pXController = static_cast<CMomentaryRotButton*>( pController );
  560. Vector vecNormalizedPos( pXController->GetPos( pXController->GetLocalAngles() ), 0.0f, 0.0f );
  561. Vector vecWorldSpace;
  562. CollisionProp()->NormalizedToWorldSpace( vecNormalizedPos, &vecWorldSpace );
  563. vecStart.x = vecWorldSpace.x;
  564. }
  565. else
  566. {
  567. DevMsg( "func_mortarfield has X controller that isn't a momentary_rot_button.\n" );
  568. }
  569. }
  570. }
  571. if ( m_iszYController != NULL_STRING )
  572. {
  573. pController = gEntList.FindEntityByName( NULL, STRING(m_iszYController) );
  574. if (pController != NULL)
  575. {
  576. if ( FClassnameIs( pController, "momentary_rot_button" ) )
  577. {
  578. CMomentaryRotButton *pYController = static_cast<CMomentaryRotButton*>( pController );
  579. Vector vecNormalizedPos( 0.0f, pYController->GetPos( pYController->GetLocalAngles() ), 0.0f );
  580. Vector vecWorldSpace;
  581. CollisionProp()->NormalizedToWorldSpace( vecNormalizedPos, &vecWorldSpace );
  582. vecStart.y = vecWorldSpace.y;
  583. }
  584. else
  585. {
  586. DevMsg( "func_mortarfield has Y controller that isn't a momentary_rot_button.\n" );
  587. }
  588. }
  589. }
  590. }
  591. break;
  592. }
  593. CPASAttenuationFilter filter( this, ATTN_NONE );
  594. EmitSound( filter, entindex(), "MortarField.Trigger" );
  595. float t = 2.5;
  596. for (int i = 0; i < m_iCount; i++)
  597. {
  598. Vector vecSpot = vecStart;
  599. vecSpot.x += random->RandomFloat( -m_flSpread, m_flSpread );
  600. vecSpot.y += random->RandomFloat( -m_flSpread, m_flSpread );
  601. trace_t tr;
  602. UTIL_TraceLine( vecSpot, vecSpot + Vector( 0, 0, -1 ) * MAX_TRACE_LENGTH, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr );
  603. CBaseEntity *pMortar = Create( "monster_mortar", tr.endpos, QAngle( 0, 0, 0 ), inputdata.pActivator );
  604. pMortar->SetNextThink( gpGlobals->curtime + t );
  605. t += random->RandomFloat( 0.2, 0.5 );
  606. if (i == 0)
  607. CSoundEnt::InsertSound ( SOUND_DANGER, tr.endpos, 400, 0.3 );
  608. }
  609. }
  610. #ifdef HL1_DLL
  611. class CMortar : public CHL1BaseGrenade
  612. {
  613. DECLARE_CLASS( CMortar, CHL1BaseGrenade );
  614. public:
  615. void Spawn( void );
  616. void Precache( void );
  617. void MortarExplode( void );
  618. int m_spriteTexture;
  619. DECLARE_DATADESC();
  620. };
  621. BEGIN_DATADESC( CMortar )
  622. DEFINE_THINKFUNC( MortarExplode ),
  623. //DEFINE_FIELD( m_spriteTexture, FIELD_INTEGER ),
  624. END_DATADESC()
  625. LINK_ENTITY_TO_CLASS( monster_mortar, CMortar );
  626. void CMortar::Spawn( )
  627. {
  628. SetMoveType( MOVETYPE_NONE );
  629. SetSolid( SOLID_NONE );
  630. SetDamage( 200 );
  631. SetDamageRadius( GetDamage() * 2.5 );
  632. SetThink( &CMortar::MortarExplode );
  633. SetNextThink( TICK_NEVER_THINK );
  634. Precache( );
  635. }
  636. void CMortar::Precache( )
  637. {
  638. m_spriteTexture = PrecacheModel( "sprites/lgtning.vmt" );
  639. }
  640. void CMortar::MortarExplode( void )
  641. {
  642. Vector vecStart = GetAbsOrigin();
  643. Vector vecEnd = vecStart;
  644. vecEnd.z += 1024;
  645. UTIL_Beam( vecStart, vecEnd, m_spriteTexture, 0, 0, 0, 0.5, 4.0, 4.0, 100, 0, 255, 160, 100, 128, 0 );
  646. trace_t tr;
  647. UTIL_TraceLine( GetAbsOrigin() + Vector( 0, 0, 1024 ), GetAbsOrigin() - Vector( 0, 0, 1024 ), MASK_ALL, this, COLLISION_GROUP_NONE, &tr );
  648. Explode( &tr, DMG_BLAST | DMG_MISSILEDEFENSE );
  649. UTIL_ScreenShake( tr.endpos, 25.0, 150.0, 1.0, 750, SHAKE_START );
  650. }
  651. #endif
  652. //=========================================================
  653. // Dead HEV suit prop
  654. //=========================================================
  655. class CNPC_DeadHEV : public CAI_BaseNPC
  656. {
  657. DECLARE_CLASS( CNPC_DeadHEV, CAI_BaseNPC );
  658. public:
  659. void Spawn( void );
  660. Class_T Classify ( void ) { return CLASS_NONE; }
  661. float MaxYawSpeed( void ) { return 8.0f; }
  662. bool KeyValue( const char *szKeyName, const char *szValue );
  663. int m_iPose;// which sequence to display -- temporary, don't need to save
  664. static char *m_szPoses[4];
  665. };
  666. char *CNPC_DeadHEV::m_szPoses[] = { "deadback", "deadsitting", "deadstomach", "deadtable" };
  667. bool CNPC_DeadHEV::KeyValue( const char *szKeyName, const char *szValue )
  668. {
  669. if ( FStrEq( szKeyName, "pose" ) )
  670. m_iPose = atoi( szValue );
  671. else
  672. CAI_BaseNPC::KeyValue( szKeyName, szValue );
  673. return true;
  674. }
  675. LINK_ENTITY_TO_CLASS( monster_hevsuit_dead, CNPC_DeadHEV );
  676. //=========================================================
  677. // ********** DeadHEV SPAWN **********
  678. //=========================================================
  679. void CNPC_DeadHEV::Spawn( void )
  680. {
  681. PrecacheModel("models/player.mdl");
  682. SetModel( "models/player.mdl" );
  683. ClearEffects();
  684. SetSequence( 0 );
  685. m_nBody = 1;
  686. m_bloodColor = BLOOD_COLOR_RED;
  687. SetSequence( LookupSequence( m_szPoses[m_iPose] ) );
  688. if ( GetSequence() == -1 )
  689. {
  690. Msg ( "Dead hevsuit with bad pose\n" );
  691. SetSequence( 0 );
  692. ClearEffects();
  693. AddEffects( EF_BRIGHTLIGHT );
  694. }
  695. // Corpses have less health
  696. m_iHealth = 8;
  697. NPCInitDead();
  698. }
  699. //
  700. // Render parameters trigger
  701. //
  702. // This entity will copy its render parameters (renderfx, rendermode, rendercolor, renderamt)
  703. // to its targets when triggered.
  704. //
  705. // Flags to indicate masking off various render parameters that are normally copied to the targets
  706. #define SF_RENDER_MASKFX (1<<0)
  707. #define SF_RENDER_MASKAMT (1<<1)
  708. #define SF_RENDER_MASKMODE (1<<2)
  709. #define SF_RENDER_MASKCOLOR (1<<3)
  710. class CRenderFxManager : public CBaseEntity
  711. {
  712. DECLARE_CLASS( CRenderFxManager, CBaseEntity );
  713. public:
  714. void Spawn( void );
  715. void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
  716. // Input handlers.
  717. void InputActivate( inputdata_t &inputdata );
  718. DECLARE_DATADESC();
  719. };
  720. BEGIN_DATADESC( CRenderFxManager )
  721. DEFINE_INPUTFUNC( FIELD_VOID, "Activate", InputActivate ),
  722. END_DATADESC()
  723. LINK_ENTITY_TO_CLASS( env_render, CRenderFxManager );
  724. void CRenderFxManager::Spawn( void )
  725. {
  726. AddSolidFlags( FSOLID_NOT_SOLID );
  727. SetMoveType( MOVETYPE_NONE );
  728. AddEffects( EF_NODRAW );
  729. }
  730. void CRenderFxManager::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
  731. {
  732. if ( m_target != NULL_STRING )
  733. {
  734. CBaseEntity *pEntity = NULL;
  735. while ( ( pEntity = gEntList.FindEntityByName( pEntity, STRING( m_target ) ) ) != NULL )
  736. {
  737. if ( !HasSpawnFlags( SF_RENDER_MASKFX ) )
  738. pEntity->m_nRenderFX = m_nRenderFX;
  739. if ( !HasSpawnFlags( SF_RENDER_MASKAMT ) )
  740. pEntity->SetRenderColorA( GetRenderColor().a );
  741. if ( !HasSpawnFlags( SF_RENDER_MASKMODE ) )
  742. pEntity->m_nRenderMode = m_nRenderMode;
  743. if ( !HasSpawnFlags( SF_RENDER_MASKCOLOR ) )
  744. pEntity->m_clrRender = m_clrRender;
  745. }
  746. }
  747. }
  748. void CRenderFxManager::InputActivate( inputdata_t &inputdata )
  749. {
  750. Use( inputdata.pActivator, inputdata.pCaller, USE_ON, 0 );
  751. }
  752. // Link env_sound to soundscape system
  753. LINK_ENTITY_TO_CLASS( env_sound, CEnvSoundscape );
  754. ///////////////////////
  755. //XEN!
  756. //////////////////////
  757. #define XEN_PLANT_GLOW_SPRITE "sprites/flare3.spr"
  758. #define XEN_PLANT_HIDE_TIME 5
  759. class CXenPLight : public CActAnimating
  760. {
  761. DECLARE_CLASS( CXenPLight, CActAnimating );
  762. public:
  763. DECLARE_DATADESC();
  764. void Spawn( void );
  765. void Precache( void );
  766. void Touch( CBaseEntity *pOther );
  767. void Think( void );
  768. void LightOn( void );
  769. void LightOff( void );
  770. float m_flDmgTime;
  771. private:
  772. CSprite *m_pGlow;
  773. };
  774. LINK_ENTITY_TO_CLASS( xen_plantlight, CXenPLight );
  775. BEGIN_DATADESC( CXenPLight )
  776. DEFINE_FIELD( m_pGlow, FIELD_CLASSPTR ),
  777. DEFINE_FIELD( m_flDmgTime, FIELD_FLOAT ),
  778. END_DATADESC()
  779. void CXenPLight::Spawn( void )
  780. {
  781. Precache();
  782. SetModel( "models/light.mdl" );
  783. SetMoveType( MOVETYPE_NONE );
  784. SetSolid( SOLID_BBOX );
  785. AddSolidFlags( FSOLID_TRIGGER | FSOLID_NOT_SOLID );
  786. UTIL_SetSize( this, Vector(-80,-80,0), Vector(80,80,32));
  787. SetActivity( ACT_IDLE );
  788. SetNextThink( gpGlobals->curtime + 0.1 );
  789. SetCycle( random->RandomFloat(0,1) );
  790. m_pGlow = CSprite::SpriteCreate( XEN_PLANT_GLOW_SPRITE, GetLocalOrigin() + Vector(0,0,(WorldAlignMins().z+WorldAlignMaxs().z)*0.5), FALSE );
  791. m_pGlow->SetTransparency( kRenderGlow, GetRenderColor().r, GetRenderColor().g, GetRenderColor().b, GetRenderColor().a, m_nRenderFX );
  792. m_pGlow->SetAttachment( this, 1 );
  793. }
  794. void CXenPLight::Precache( void )
  795. {
  796. PrecacheModel( "models/light.mdl" );
  797. PrecacheModel( XEN_PLANT_GLOW_SPRITE );
  798. }
  799. void CXenPLight::Think( void )
  800. {
  801. StudioFrameAdvance();
  802. SetNextThink( gpGlobals->curtime + 0.1 );
  803. switch( GetActivity() )
  804. {
  805. case ACT_CROUCH:
  806. if ( IsSequenceFinished() )
  807. {
  808. SetActivity( ACT_CROUCHIDLE );
  809. LightOff();
  810. }
  811. break;
  812. case ACT_CROUCHIDLE:
  813. if ( gpGlobals->curtime > m_flDmgTime )
  814. {
  815. SetActivity( ACT_STAND );
  816. LightOn();
  817. }
  818. break;
  819. case ACT_STAND:
  820. if ( IsSequenceFinished() )
  821. SetActivity( ACT_IDLE );
  822. break;
  823. case ACT_IDLE:
  824. default:
  825. break;
  826. }
  827. }
  828. void CXenPLight::Touch( CBaseEntity *pOther )
  829. {
  830. if ( pOther->IsPlayer() )
  831. {
  832. m_flDmgTime = gpGlobals->curtime + XEN_PLANT_HIDE_TIME;
  833. if ( GetActivity() == ACT_IDLE || GetActivity() == ACT_STAND )
  834. {
  835. SetActivity( ACT_CROUCH );
  836. }
  837. }
  838. }
  839. void CXenPLight::LightOn( void )
  840. {
  841. variant_t Value;
  842. g_EventQueue.AddEvent( STRING( m_target ), "TurnOn", Value, 0, this, this );
  843. if ( m_pGlow )
  844. m_pGlow->RemoveEffects( EF_NODRAW );
  845. }
  846. void CXenPLight::LightOff( void )
  847. {
  848. variant_t Value;
  849. g_EventQueue.AddEvent( STRING( m_target ), "TurnOff", Value, 0, this, this );
  850. if ( m_pGlow )
  851. m_pGlow->AddEffects( EF_NODRAW );
  852. }
  853. class CXenHair : public CActAnimating
  854. {
  855. DECLARE_CLASS( CXenHair, CActAnimating );
  856. public:
  857. void Spawn( void );
  858. void Precache( void );
  859. void Think( void );
  860. };
  861. LINK_ENTITY_TO_CLASS( xen_hair, CXenHair );
  862. #define SF_HAIR_SYNC 0x0001
  863. void CXenHair::Spawn( void )
  864. {
  865. Precache();
  866. SetModel( "models/hair.mdl" );
  867. UTIL_SetSize( this, Vector(-4,-4,0), Vector(4,4,32));
  868. SetSequence( 0 );
  869. if ( !HasSpawnFlags( SF_HAIR_SYNC ) )
  870. {
  871. SetCycle( random->RandomFloat( 0,1) );
  872. m_flPlaybackRate = random->RandomFloat( 0.7, 1.4 );
  873. }
  874. ResetSequenceInfo( );
  875. SetSolid( SOLID_NONE );
  876. SetMoveType( MOVETYPE_NONE );
  877. SetNextThink( gpGlobals->curtime + random->RandomFloat( 0.1, 0.4 ) ); // Load balance these a bit
  878. }
  879. void CXenHair::Think( void )
  880. {
  881. StudioFrameAdvance();
  882. SetNextThink( gpGlobals->curtime + 0.1 );
  883. }
  884. void CXenHair::Precache( void )
  885. {
  886. PrecacheModel( "models/hair.mdl" );
  887. }
  888. class CXenTreeTrigger : public CBaseEntity
  889. {
  890. DECLARE_CLASS( CXenTreeTrigger, CBaseEntity );
  891. public:
  892. void Touch( CBaseEntity *pOther );
  893. static CXenTreeTrigger *TriggerCreate( CBaseEntity *pOwner, const Vector &position );
  894. };
  895. LINK_ENTITY_TO_CLASS( xen_ttrigger, CXenTreeTrigger );
  896. CXenTreeTrigger *CXenTreeTrigger::TriggerCreate( CBaseEntity *pOwner, const Vector &position )
  897. {
  898. CXenTreeTrigger *pTrigger = CREATE_ENTITY( CXenTreeTrigger, "xen_ttrigger" );
  899. pTrigger->SetAbsOrigin( position );
  900. pTrigger->SetSolid( SOLID_BBOX );
  901. pTrigger->AddSolidFlags( FSOLID_TRIGGER | FSOLID_NOT_SOLID );
  902. pTrigger->SetMoveType( MOVETYPE_NONE );
  903. pTrigger->SetOwnerEntity( pOwner );
  904. return pTrigger;
  905. }
  906. void CXenTreeTrigger::Touch( CBaseEntity *pOther )
  907. {
  908. if ( GetOwnerEntity() )
  909. {
  910. GetOwnerEntity()->Touch( pOther );
  911. }
  912. }
  913. #define TREE_AE_ATTACK 1
  914. class CXenTree : public CActAnimating
  915. {
  916. DECLARE_CLASS( CXenTree, CActAnimating );
  917. public:
  918. void Spawn( void );
  919. void Precache( void );
  920. void Touch( CBaseEntity *pOther );
  921. void Think( void );
  922. int OnTakeDamage( const CTakeDamageInfo &info ) { Attack(); return 0; }
  923. void HandleAnimEvent( animevent_t *pEvent );
  924. void Attack( void );
  925. Class_T Classify( void ) { return CLASS_ALIEN_PREDATOR; }
  926. DECLARE_DATADESC();
  927. private:
  928. CXenTreeTrigger *m_pTrigger;
  929. };
  930. LINK_ENTITY_TO_CLASS( xen_tree, CXenTree );
  931. BEGIN_DATADESC( CXenTree )
  932. DEFINE_FIELD( m_pTrigger, FIELD_CLASSPTR ),
  933. END_DATADESC()
  934. void CXenTree::Spawn( void )
  935. {
  936. Precache();
  937. SetModel( "models/tree.mdl" );
  938. SetMoveType( MOVETYPE_NONE );
  939. SetSolid ( SOLID_BBOX );
  940. m_takedamage = DAMAGE_YES;
  941. UTIL_SetSize( this, Vector(-30,-30,0), Vector(30,30,188));
  942. SetActivity( ACT_IDLE );
  943. SetNextThink( gpGlobals->curtime + 0.1 );
  944. SetCycle( random->RandomFloat( 0,1 ) );
  945. m_flPlaybackRate = random->RandomFloat( 0.7, 1.4 );
  946. Vector triggerPosition, vForward;
  947. AngleVectors( GetAbsAngles(), &vForward );
  948. triggerPosition = GetAbsOrigin() + (vForward * 64);
  949. // Create the trigger
  950. m_pTrigger = CXenTreeTrigger::TriggerCreate( this, triggerPosition );
  951. UTIL_SetSize( m_pTrigger, Vector( -24, -24, 0 ), Vector( 24, 24, 128 ) );
  952. }
  953. void CXenTree::Precache( void )
  954. {
  955. PrecacheModel( "models/tree.mdl" );
  956. PrecacheModel( XEN_PLANT_GLOW_SPRITE );
  957. PrecacheScriptSound( "XenTree.AttackMiss" );
  958. PrecacheScriptSound( "XenTree.AttackHit" );
  959. }
  960. void CXenTree::Touch( CBaseEntity *pOther )
  961. {
  962. if ( !pOther->IsPlayer() && FClassnameIs( pOther, "monster_bigmomma" ) )
  963. return;
  964. Attack();
  965. }
  966. void CXenTree::Attack( void )
  967. {
  968. if ( GetActivity() == ACT_IDLE )
  969. {
  970. SetActivity( ACT_MELEE_ATTACK1 );
  971. m_flPlaybackRate = random->RandomFloat( 1.0, 1.4 );
  972. CPASAttenuationFilter filter( this );
  973. EmitSound( filter, entindex(), "XenTree.AttackMiss" );
  974. }
  975. }
  976. void CXenTree::HandleAnimEvent( animevent_t *pEvent )
  977. {
  978. switch( pEvent->event )
  979. {
  980. case TREE_AE_ATTACK:
  981. {
  982. CBaseEntity *pList[8];
  983. BOOL sound = FALSE;
  984. int count = UTIL_EntitiesInBox( pList, 8, m_pTrigger->GetAbsOrigin() + m_pTrigger->WorldAlignMins(), m_pTrigger->GetAbsOrigin() + m_pTrigger->WorldAlignMaxs(), FL_NPC|FL_CLIENT );
  985. Vector forward;
  986. AngleVectors( GetAbsAngles(), &forward );
  987. for ( int i = 0; i < count; i++ )
  988. {
  989. if ( pList[i] != this )
  990. {
  991. if ( pList[i]->GetOwnerEntity() != this )
  992. {
  993. sound = true;
  994. pList[i]->TakeDamage( CTakeDamageInfo(this, this, 25, DMG_CRUSH | DMG_SLASH ) );
  995. pList[i]->ViewPunch( QAngle( 15, 0, 18 ) );
  996. pList[i]->SetAbsVelocity( pList[i]->GetAbsVelocity() + forward * 100 );
  997. }
  998. }
  999. }
  1000. if ( sound )
  1001. {
  1002. CPASAttenuationFilter filter( this );
  1003. EmitSound( filter, entindex(), "XenTree.AttackHit" );
  1004. }
  1005. }
  1006. return;
  1007. }
  1008. BaseClass::HandleAnimEvent( pEvent );
  1009. }
  1010. void CXenTree::Think( void )
  1011. {
  1012. StudioFrameAdvance();
  1013. SetNextThink( gpGlobals->curtime + 0.1 );
  1014. DispatchAnimEvents( this );
  1015. switch( GetActivity() )
  1016. {
  1017. case ACT_MELEE_ATTACK1:
  1018. if ( IsSequenceFinished() )
  1019. {
  1020. SetActivity( ACT_IDLE );
  1021. m_flPlaybackRate = random->RandomFloat( 0.6f, 1.4f );
  1022. }
  1023. break;
  1024. default:
  1025. case ACT_IDLE:
  1026. break;
  1027. }
  1028. }
  1029. class CXenSpore : public CActAnimating
  1030. {
  1031. DECLARE_CLASS( CXenSpore, CActAnimating );
  1032. public:
  1033. void Spawn( void );
  1034. void Precache( void );
  1035. void Touch( CBaseEntity *pOther );
  1036. // void HandleAnimEvent( MonsterEvent_t *pEvent );
  1037. void Attack( void ) {}
  1038. static const char *pModelNames[];
  1039. };
  1040. class CXenSporeSmall : public CXenSpore
  1041. {
  1042. DECLARE_CLASS( CXenSporeSmall, CXenSpore );
  1043. void Spawn( void );
  1044. };
  1045. class CXenSporeMed : public CXenSpore
  1046. {
  1047. DECLARE_CLASS( CXenSporeMed, CXenSpore );
  1048. void Spawn( void );
  1049. };
  1050. class CXenSporeLarge : public CXenSpore
  1051. {
  1052. DECLARE_CLASS( CXenSporeLarge, CXenSpore );
  1053. void Spawn( void );
  1054. static const Vector m_hullSizes[];
  1055. };
  1056. // Fake collision box for big spores
  1057. class CXenHull : public CPointEntity
  1058. {
  1059. DECLARE_CLASS( CXenHull, CPointEntity );
  1060. public:
  1061. static CXenHull *CreateHull( CBaseEntity *source, const Vector &mins, const Vector &maxs, const Vector &offset );
  1062. Class_T Classify( void ) { return CLASS_ALIEN_PREDATOR; }
  1063. };
  1064. CXenHull *CXenHull::CreateHull( CBaseEntity *source, const Vector &mins, const Vector &maxs, const Vector &offset )
  1065. {
  1066. CXenHull *pHull = CREATE_ENTITY( CXenHull, "xen_hull" );
  1067. UTIL_SetOrigin( pHull, source->GetAbsOrigin() + offset );
  1068. pHull->SetSolid( SOLID_BBOX );
  1069. pHull->SetMoveType( MOVETYPE_NONE );
  1070. pHull->SetOwnerEntity( source );
  1071. UTIL_SetSize( pHull, mins, maxs );
  1072. pHull->SetRenderColorA( 0 );
  1073. pHull->m_nRenderMode = kRenderTransTexture;
  1074. return pHull;
  1075. }
  1076. LINK_ENTITY_TO_CLASS( xen_spore_small, CXenSporeSmall );
  1077. LINK_ENTITY_TO_CLASS( xen_spore_medium, CXenSporeMed );
  1078. LINK_ENTITY_TO_CLASS( xen_spore_large, CXenSporeLarge );
  1079. LINK_ENTITY_TO_CLASS( xen_hull, CXenHull );
  1080. void CXenSporeSmall::Spawn( void )
  1081. {
  1082. m_nSkin = 0;
  1083. CXenSpore::Spawn();
  1084. UTIL_SetSize( this, Vector(-16,-16,0), Vector(16,16,64));
  1085. }
  1086. void CXenSporeMed::Spawn( void )
  1087. {
  1088. m_nSkin = 1;
  1089. CXenSpore::Spawn();
  1090. UTIL_SetSize( this, Vector(-40,-40,0), Vector(40,40,120));
  1091. }
  1092. // I just eyeballed these -- fill in hulls for the legs
  1093. const Vector CXenSporeLarge::m_hullSizes[] =
  1094. {
  1095. Vector( 90, -25, 0 ),
  1096. Vector( 25, 75, 0 ),
  1097. Vector( -15, -100, 0 ),
  1098. Vector( -90, -35, 0 ),
  1099. Vector( -90, 60, 0 ),
  1100. };
  1101. void CXenSporeLarge::Spawn( void )
  1102. {
  1103. m_nSkin = 2;
  1104. CXenSpore::Spawn();
  1105. UTIL_SetSize( this, Vector(-48,-48,110), Vector(48,48,240));
  1106. Vector forward, right;
  1107. AngleVectors( GetAbsAngles(), &forward, &right, NULL );
  1108. // Rotate the leg hulls into position
  1109. for ( int i = 0; i < ARRAYSIZE(m_hullSizes); i++ )
  1110. {
  1111. CXenHull::CreateHull( this, Vector(-12, -12, 0 ), Vector( 12, 12, 120 ), (m_hullSizes[i].x * forward) + (m_hullSizes[i].y * right) );
  1112. }
  1113. }
  1114. void CXenSpore::Spawn( void )
  1115. {
  1116. Precache();
  1117. SetModel( pModelNames[m_nSkin] );
  1118. SetMoveType( MOVETYPE_NONE );
  1119. SetSolid( SOLID_BBOX );
  1120. m_takedamage = DAMAGE_NO;
  1121. // SetActivity( ACT_IDLE );
  1122. SetSequence( 0 );
  1123. SetCycle( random->RandomFloat( 0.0f, 1.0f ) );
  1124. m_flPlaybackRate = random->RandomFloat( 0.7f, 1.4f );
  1125. ResetSequenceInfo( );
  1126. SetNextThink( gpGlobals->curtime + random->RandomFloat( 0.1f, 0.4f ) ); // Load balance these a bit
  1127. }
  1128. const char *CXenSpore::pModelNames[] =
  1129. {
  1130. "models/fungus(small).mdl",
  1131. "models/fungus.mdl",
  1132. "models/fungus(large).mdl",
  1133. };
  1134. void CXenSpore::Precache( void )
  1135. {
  1136. PrecacheModel( (char *)pModelNames[m_nSkin] );
  1137. }
  1138. void CXenSpore::Touch( CBaseEntity *pOther )
  1139. {
  1140. }
  1141. //=========================================================
  1142. // WaitTillLand - in order to emit their meaty scent from
  1143. // the proper location, gibs should wait until they stop
  1144. // bouncing to emit their scent. That's what this function
  1145. // does.
  1146. //=========================================================
  1147. void CHL1Gib::WaitTillLand ( void )
  1148. {
  1149. if ( !IsInWorld() )
  1150. {
  1151. UTIL_Remove( this );
  1152. return;
  1153. }
  1154. if ( GetAbsVelocity() == vec3_origin )
  1155. {
  1156. /* SetRenderColorA( 255 );
  1157. m_nRenderMode = kRenderTransTexture;
  1158. AddSolidFlags( FSOLID_NOT_SOLID );*/
  1159. SetNextThink( gpGlobals->curtime + m_lifeTime );
  1160. SetThink ( &CBaseEntity::SUB_FadeOut );
  1161. // If you bleed, you stink!
  1162. /* if ( m_bloodColor != DONT_BLEED )
  1163. {
  1164. // ok, start stinkin!
  1165. CSoundEnt::InsertSound ( bits_SOUND_MEAT, pev->origin, 384, 25 );
  1166. }*/
  1167. }
  1168. else
  1169. {
  1170. // wait and check again in another half second.
  1171. SetNextThink( gpGlobals->curtime + 0.5 );
  1172. }
  1173. }
  1174. //
  1175. // Gib bounces on the ground or wall, sponges some blood down, too!
  1176. //
  1177. void CHL1Gib::BounceGibTouch ( CBaseEntity *pOther )
  1178. {
  1179. Vector vecSpot;
  1180. trace_t tr;
  1181. if ( GetFlags() & FL_ONGROUND)
  1182. {
  1183. SetAbsVelocity( GetAbsVelocity() * 0.9 );
  1184. SetAbsAngles( QAngle( 0, GetAbsAngles().y, 0 ) );
  1185. SetLocalAngularVelocity( QAngle( 0, GetLocalAngularVelocity().y, 0 ) );
  1186. }
  1187. else
  1188. {
  1189. if ( m_cBloodDecals > 0 && m_bloodColor != DONT_BLEED )
  1190. {
  1191. vecSpot = GetAbsOrigin() + Vector ( 0 , 0 , 8 );//move up a bit, and trace down.
  1192. UTIL_TraceLine ( vecSpot, vecSpot + Vector ( 0, 0, -24 ), MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr);
  1193. UTIL_BloodDecalTrace( &tr, m_bloodColor );
  1194. m_cBloodDecals--;
  1195. }
  1196. if ( m_material != matNone && random->RandomInt( 0, 2 ) == 0 )
  1197. {
  1198. float volume;
  1199. float zvel = fabs( GetAbsVelocity().z );
  1200. volume = 0.8 * MIN(1.0, ((float)zvel) / 450.0);
  1201. CBreakable::MaterialSoundRandom( entindex(), (Materials)m_material, volume );
  1202. }
  1203. }
  1204. }
  1205. //
  1206. // Sticky gib puts blood on the wall and stays put.
  1207. //
  1208. void CHL1Gib::StickyGibTouch ( CBaseEntity *pOther )
  1209. {
  1210. Vector vecSpot;
  1211. trace_t tr;
  1212. SetThink ( &CHL1Gib::SUB_Remove );
  1213. SetNextThink( gpGlobals->curtime + 10 );
  1214. if ( !FClassnameIs( pOther, "worldspawn" ) )
  1215. {
  1216. SetNextThink( gpGlobals->curtime );
  1217. return;
  1218. }
  1219. UTIL_TraceLine ( GetAbsOrigin(), GetAbsOrigin() + GetAbsVelocity() * 32, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr );
  1220. UTIL_BloodDecalTrace( &tr, m_bloodColor );
  1221. SetAbsVelocity( tr.plane.normal * -1 );
  1222. QAngle qAngle;
  1223. VectorAngles( GetAbsVelocity(), qAngle );
  1224. SetAbsAngles( qAngle );
  1225. SetAbsVelocity( vec3_origin );
  1226. SetLocalAngularVelocity( QAngle( 0, 0, 0 ) );
  1227. SetMoveType( MOVETYPE_NONE );
  1228. }
  1229. //
  1230. // Throw a chunk
  1231. //
  1232. void CHL1Gib::Spawn( const char *szGibModel )
  1233. {
  1234. SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE );
  1235. SetFriction( 0.55 ); // deading the bounce a bit
  1236. // sometimes an entity inherits the edict from a former piece of glass,
  1237. // and will spawn using the same render FX or rendermode! bad!
  1238. SetRenderColorA( 255 );
  1239. m_nRenderMode = kRenderNormal;
  1240. m_nRenderFX = kRenderFxNone;
  1241. SetSolid( SOLID_BBOX );
  1242. AddSolidFlags( FSOLID_NOT_STANDABLE );
  1243. SetClassname( "gib" );
  1244. SetModel( szGibModel );
  1245. UTIL_SetSize( this, Vector( 0, 0, 0), Vector(0, 0, 0));
  1246. SetNextThink( gpGlobals->curtime + 4 );
  1247. m_lifeTime = 25;
  1248. SetThink ( &CHL1Gib::WaitTillLand );
  1249. SetTouch ( &CHL1Gib::BounceGibTouch );
  1250. m_material = matNone;
  1251. m_cBloodDecals = 5;// how many blood decals this gib can place (1 per bounce until none remain).
  1252. }
  1253. LINK_ENTITY_TO_CLASS( hl1gib, CHL1Gib );
  1254. BEGIN_DATADESC( CHL1Gib )
  1255. // Function Pointers
  1256. DEFINE_FUNCTION( BounceGibTouch ),
  1257. DEFINE_FUNCTION( StickyGibTouch ),
  1258. DEFINE_FUNCTION( WaitTillLand ),
  1259. DEFINE_FIELD( m_bloodColor, FIELD_INTEGER ),
  1260. DEFINE_FIELD( m_cBloodDecals, FIELD_INTEGER ),
  1261. DEFINE_FIELD( m_material, FIELD_INTEGER ),
  1262. DEFINE_FIELD( m_lifeTime, FIELD_FLOAT ),
  1263. END_DATADESC()
  1264. #define SF_ENDSECTION_USEONLY 0x0001
  1265. class CTriggerEndSection : public CBaseEntity
  1266. {
  1267. DECLARE_CLASS( CTriggerEndSection, CBaseEntity );
  1268. public:
  1269. void Spawn( void );
  1270. void InputEndSection( inputdata_t &data );
  1271. DECLARE_DATADESC();
  1272. };
  1273. LINK_ENTITY_TO_CLASS( trigger_endsection, CTriggerEndSection );
  1274. BEGIN_DATADESC( CTriggerEndSection )
  1275. DEFINE_INPUTFUNC( FIELD_VOID, "EndSection", InputEndSection ),
  1276. END_DATADESC()
  1277. void CTriggerEndSection::Spawn( void )
  1278. {
  1279. if ( gpGlobals->deathmatch )
  1280. {
  1281. UTIL_Remove( this );
  1282. return;
  1283. }
  1284. }
  1285. void CTriggerEndSection::InputEndSection( inputdata_t &data )
  1286. {
  1287. CBaseEntity *pPlayer = UTIL_GetLocalPlayer();
  1288. if ( pPlayer )
  1289. {
  1290. //HACKY MCHACK - This works, but it's nasty. Alfred is going to fix a
  1291. //bug in gameui that prevents you from dropping to the main menu after
  1292. // calling disconnect.
  1293. engine->ClientCommand ( pPlayer->edict(), "toggleconsole;disconnect\n");
  1294. }
  1295. UTIL_Remove( this );
  1296. }