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.

762 lines
18 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Implements visual effects entities: sprites, beams, bubbles, etc.
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "beam_shared.h"
  9. #include "ndebugoverlay.h"
  10. #include "filters.h"
  11. // memdbgon must be the last include file in a .cpp file!!!
  12. #include "tier0/memdbgon.h"
  13. // Keeps us from doing strcmps in the tracefilter.
  14. string_t g_iszPhysicsPropClassname;
  15. enum Touch_t
  16. {
  17. touch_none = 0,
  18. touch_player_only,
  19. touch_npc_only,
  20. touch_player_or_npc,
  21. touch_player_or_npc_or_physicsprop,
  22. };
  23. class CEnvBeam : public CBeam
  24. {
  25. public:
  26. DECLARE_CLASS( CEnvBeam, CBeam );
  27. void Spawn( void );
  28. void Precache( void );
  29. void Activate( void );
  30. void StrikeThink( void );
  31. void UpdateThink( void );
  32. void RandomArea( void );
  33. void RandomPoint( const Vector &vecSrc );
  34. void Zap( const Vector &vecSrc, const Vector &vecDest );
  35. void Strike( void );
  36. bool PassesTouchFilters(CBaseEntity *pOther);
  37. void InputTurnOn( inputdata_t &inputdata );
  38. void InputTurnOff( inputdata_t &inputdata );
  39. void InputToggle( inputdata_t &inputdata );
  40. void InputStrikeOnce( inputdata_t &inputdata );
  41. void TurnOn( void );
  42. void TurnOff( void );
  43. void Toggle( void );
  44. const char *GetDecalName( void ){ return STRING( m_iszDecal );}
  45. inline bool ServerSide( void )
  46. {
  47. if ( m_life == 0 && !HasSpawnFlags(SF_BEAM_RING) )
  48. return true;
  49. return false;
  50. }
  51. DECLARE_DATADESC();
  52. void BeamUpdateVars( void );
  53. int m_active;
  54. int m_spriteTexture;
  55. string_t m_iszStartEntity;
  56. string_t m_iszEndEntity;
  57. float m_life;
  58. float m_boltWidth;
  59. float m_noiseAmplitude;
  60. int m_speed;
  61. float m_restrike;
  62. string_t m_iszSpriteName;
  63. int m_frameStart;
  64. float m_radius;
  65. Touch_t m_TouchType;
  66. string_t m_iFilterName;
  67. EHANDLE m_hFilter;
  68. string_t m_iszDecal;
  69. COutputEvent m_OnTouchedByEntity;
  70. };
  71. LINK_ENTITY_TO_CLASS( env_beam, CEnvBeam );
  72. BEGIN_DATADESC( CEnvBeam )
  73. DEFINE_FIELD( m_active, FIELD_INTEGER ),
  74. DEFINE_FIELD( m_spriteTexture, FIELD_INTEGER ),
  75. DEFINE_KEYFIELD( m_iszStartEntity, FIELD_STRING, "LightningStart" ),
  76. DEFINE_KEYFIELD( m_iszEndEntity, FIELD_STRING, "LightningEnd" ),
  77. DEFINE_KEYFIELD( m_life, FIELD_FLOAT, "life" ),
  78. DEFINE_KEYFIELD( m_boltWidth, FIELD_FLOAT, "BoltWidth" ),
  79. DEFINE_KEYFIELD( m_noiseAmplitude, FIELD_FLOAT, "NoiseAmplitude" ),
  80. DEFINE_KEYFIELD( m_speed, FIELD_INTEGER, "TextureScroll" ),
  81. DEFINE_KEYFIELD( m_restrike, FIELD_FLOAT, "StrikeTime" ),
  82. DEFINE_KEYFIELD( m_iszSpriteName, FIELD_STRING, "texture" ),
  83. DEFINE_KEYFIELD( m_frameStart, FIELD_INTEGER, "framestart" ),
  84. DEFINE_KEYFIELD( m_radius, FIELD_FLOAT, "Radius" ),
  85. DEFINE_KEYFIELD( m_TouchType, FIELD_INTEGER, "TouchType" ),
  86. DEFINE_KEYFIELD( m_iFilterName, FIELD_STRING, "filtername" ),
  87. DEFINE_KEYFIELD( m_iszDecal, FIELD_STRING, "decalname" ),
  88. DEFINE_FIELD( m_hFilter, FIELD_EHANDLE ),
  89. // Function Pointers
  90. DEFINE_FUNCTION( StrikeThink ),
  91. DEFINE_FUNCTION( UpdateThink ),
  92. // Input functions
  93. DEFINE_INPUTFUNC( FIELD_VOID, "TurnOn", InputTurnOn ),
  94. DEFINE_INPUTFUNC( FIELD_VOID, "TurnOff", InputTurnOff ),
  95. DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ),
  96. DEFINE_INPUTFUNC( FIELD_VOID, "StrikeOnce", InputStrikeOnce ),
  97. DEFINE_OUTPUT( m_OnTouchedByEntity, "OnTouchedByEntity" ),
  98. END_DATADESC()
  99. //-----------------------------------------------------------------------------
  100. // Purpose:
  101. //-----------------------------------------------------------------------------
  102. void CEnvBeam::Spawn( void )
  103. {
  104. if ( !m_iszSpriteName )
  105. {
  106. SetThink( &CEnvBeam::SUB_Remove );
  107. return;
  108. }
  109. BaseClass::Spawn();
  110. m_noiseAmplitude = MIN(MAX_BEAM_NOISEAMPLITUDE, m_noiseAmplitude);
  111. // Check for tapering
  112. if ( HasSpawnFlags( SF_BEAM_TAPEROUT ) )
  113. {
  114. SetWidth( m_boltWidth );
  115. SetEndWidth( 0 );
  116. }
  117. else
  118. {
  119. SetWidth( m_boltWidth );
  120. SetEndWidth( GetWidth() ); // Note: EndWidth is not scaled
  121. }
  122. if ( ServerSide() )
  123. {
  124. SetThink( &CEnvBeam::UpdateThink );
  125. SetNextThink( gpGlobals->curtime );
  126. SetFireTime( gpGlobals->curtime );
  127. if ( GetEntityName() != NULL_STRING )
  128. {
  129. if ( !(m_spawnflags & SF_BEAM_STARTON) )
  130. {
  131. AddEffects( EF_NODRAW );
  132. m_active = 0;
  133. SetNextThink( TICK_NEVER_THINK );
  134. }
  135. else
  136. {
  137. m_active = 1;
  138. }
  139. }
  140. }
  141. else
  142. {
  143. m_active = 0;
  144. if ( !GetEntityName() || FBitSet(m_spawnflags, SF_BEAM_STARTON) )
  145. {
  146. SetThink( &CEnvBeam::StrikeThink );
  147. SetNextThink( gpGlobals->curtime + 1.0f );
  148. }
  149. }
  150. }
  151. //-----------------------------------------------------------------------------
  152. // Purpose:
  153. //-----------------------------------------------------------------------------
  154. void CEnvBeam::Precache( void )
  155. {
  156. if ( !Q_stristr( STRING(m_iszSpriteName), ".vmt" ) )
  157. {
  158. // HACK/YWB: This was almost always the laserbeam.spr, so alloc'ing the name a second time with the proper extension isn't going to
  159. // kill us on memrory.
  160. //Warning( "Level Design Error: %s (%i:%s) Sprite name (%s) missing .vmt extension!\n",
  161. // STRING( m_iClassname ), entindex(), GetEntityName(), STRING(m_iszSpriteName) );
  162. char fixedname[ 512 ];
  163. Q_strncpy( fixedname, STRING( m_iszSpriteName ), sizeof( fixedname ) );
  164. Q_SetExtension( fixedname, ".vmt", sizeof( fixedname ) );
  165. m_iszSpriteName = AllocPooledString( fixedname );
  166. }
  167. g_iszPhysicsPropClassname = AllocPooledString( "prop_physics" );
  168. m_spriteTexture = PrecacheModel( STRING(m_iszSpriteName) );
  169. BaseClass::Precache();
  170. }
  171. //-----------------------------------------------------------------------------
  172. // Purpose:
  173. //-----------------------------------------------------------------------------
  174. void CEnvBeam::Activate( void )
  175. {
  176. // Get a handle to my filter entity if there is one
  177. if (m_iFilterName != NULL_STRING)
  178. {
  179. m_hFilter = dynamic_cast<CBaseFilter *>(gEntList.FindEntityByName( NULL, m_iFilterName ));
  180. }
  181. BaseClass::Activate();
  182. if ( ServerSide() )
  183. BeamUpdateVars();
  184. }
  185. //-----------------------------------------------------------------------------
  186. // Purpose: Input handler to turn the lightning on either continually or for
  187. // interval refiring.
  188. //-----------------------------------------------------------------------------
  189. void CEnvBeam::InputTurnOn( inputdata_t &inputdata )
  190. {
  191. if ( !m_active )
  192. {
  193. TurnOn();
  194. }
  195. }
  196. //-----------------------------------------------------------------------------
  197. // Purpose: Input handler to turn the lightning off.
  198. //-----------------------------------------------------------------------------
  199. void CEnvBeam::InputTurnOff( inputdata_t &inputdata )
  200. {
  201. if ( m_active )
  202. {
  203. TurnOff();
  204. }
  205. }
  206. //-----------------------------------------------------------------------------
  207. // Purpose: Input handler to toggle the lightning on/off.
  208. //-----------------------------------------------------------------------------
  209. void CEnvBeam::InputToggle( inputdata_t &inputdata )
  210. {
  211. if ( m_active )
  212. {
  213. TurnOff();
  214. }
  215. else
  216. {
  217. TurnOn();
  218. }
  219. }
  220. //-----------------------------------------------------------------------------
  221. // Purpose: Input handler for making the beam strike once. This will not affect
  222. // any interval refiring that might be going on. If the lifetime is set
  223. // to zero (infinite) it will turn on and stay on.
  224. //-----------------------------------------------------------------------------
  225. void CEnvBeam::InputStrikeOnce( inputdata_t &inputdata )
  226. {
  227. Strike();
  228. }
  229. //-----------------------------------------------------------------------------
  230. // Purpose: Turns the lightning on. If it is set for interval refiring, it will
  231. // begin doing so. If it is set to be continually on, it will do so.
  232. //-----------------------------------------------------------------------------
  233. void CEnvBeam::TurnOn( void )
  234. {
  235. m_active = 1;
  236. if ( ServerSide() )
  237. {
  238. RemoveEffects( EF_NODRAW );
  239. DoSparks( GetAbsStartPos(), GetAbsEndPos() );
  240. SetThink( &CEnvBeam::UpdateThink );
  241. SetNextThink( gpGlobals->curtime );
  242. SetFireTime( gpGlobals->curtime );
  243. }
  244. else
  245. {
  246. SetThink( &CEnvBeam::StrikeThink );
  247. SetNextThink( gpGlobals->curtime );
  248. }
  249. }
  250. //-----------------------------------------------------------------------------
  251. // Purpose:
  252. //-----------------------------------------------------------------------------
  253. void CEnvBeam::TurnOff( void )
  254. {
  255. m_active = 0;
  256. if ( ServerSide() )
  257. {
  258. AddEffects( EF_NODRAW );
  259. }
  260. SetNextThink( TICK_NEVER_THINK );
  261. SetThink( NULL );
  262. }
  263. //-----------------------------------------------------------------------------
  264. // Purpose: Think function for striking at intervals.
  265. //-----------------------------------------------------------------------------
  266. void CEnvBeam::StrikeThink( void )
  267. {
  268. if ( m_life != 0 )
  269. {
  270. if ( m_spawnflags & SF_BEAM_RANDOM )
  271. SetNextThink( gpGlobals->curtime + m_life + random->RandomFloat( 0, m_restrike ) );
  272. else
  273. SetNextThink( gpGlobals->curtime + m_life + m_restrike );
  274. }
  275. m_active = 1;
  276. if (!m_iszEndEntity)
  277. {
  278. if (!m_iszStartEntity)
  279. {
  280. RandomArea( );
  281. }
  282. else
  283. {
  284. CBaseEntity *pStart = RandomTargetname( STRING(m_iszStartEntity) );
  285. if (pStart != NULL)
  286. {
  287. RandomPoint( pStart->GetAbsOrigin() );
  288. }
  289. else
  290. {
  291. Msg( "env_beam: unknown entity \"%s\"\n", STRING(m_iszStartEntity) );
  292. }
  293. }
  294. return;
  295. }
  296. Strike();
  297. }
  298. //-----------------------------------------------------------------------------
  299. // Purpose: Strikes once for its configured lifetime.
  300. //-----------------------------------------------------------------------------
  301. void CEnvBeam::Strike( void )
  302. {
  303. CBroadcastRecipientFilter filter;
  304. CBaseEntity *pStart = RandomTargetname( STRING(m_iszStartEntity) );
  305. CBaseEntity *pEnd = RandomTargetname( STRING(m_iszEndEntity) );
  306. if ( pStart == NULL || pEnd == NULL )
  307. return;
  308. m_speed = clamp( (int) m_speed, 0, (int) MAX_BEAM_SCROLLSPEED );
  309. int pointStart = IsStaticPointEntity( pStart );
  310. int pointEnd = IsStaticPointEntity( pEnd );
  311. if ( pointStart || pointEnd )
  312. {
  313. if ( m_spawnflags & SF_BEAM_RING )
  314. {
  315. // don't work
  316. return;
  317. }
  318. te->BeamEntPoint( filter, 0.0,
  319. pointStart ? 0 : pStart->entindex(),
  320. pointStart ? &pStart->GetAbsOrigin() : NULL,
  321. pointEnd ? 0 : pEnd->entindex(),
  322. pointEnd ? &pEnd->GetAbsOrigin() : NULL,
  323. m_spriteTexture,
  324. 0, // No halo
  325. m_frameStart,
  326. (int)m_flFrameRate,
  327. m_life,
  328. m_boltWidth,
  329. m_boltWidth, // End width
  330. 0, // No fade
  331. m_noiseAmplitude,
  332. m_clrRender->r, m_clrRender->g, m_clrRender->b, m_clrRender->a,
  333. m_speed );
  334. }
  335. else
  336. {
  337. if ( m_spawnflags & SF_BEAM_RING)
  338. {
  339. te->BeamRing( filter, 0.0,
  340. pStart->entindex(),
  341. pEnd->entindex(),
  342. m_spriteTexture,
  343. 0, // No halo
  344. m_frameStart,
  345. (int)m_flFrameRate,
  346. m_life,
  347. m_boltWidth,
  348. 0, // No spread
  349. m_noiseAmplitude,
  350. m_clrRender->r,
  351. m_clrRender->g,
  352. m_clrRender->b,
  353. m_clrRender->a,
  354. m_speed );
  355. }
  356. else
  357. {
  358. te->BeamEnts( filter, 0.0,
  359. pStart->entindex(),
  360. pEnd->entindex(),
  361. m_spriteTexture,
  362. 0, // No halo
  363. m_frameStart,
  364. (int)m_flFrameRate,
  365. m_life,
  366. m_boltWidth,
  367. m_boltWidth, // End width
  368. 0, // No fade
  369. m_noiseAmplitude,
  370. m_clrRender->r,
  371. m_clrRender->g,
  372. m_clrRender->b,
  373. m_clrRender->a,
  374. m_speed );
  375. }
  376. }
  377. DoSparks( pStart->GetAbsOrigin(), pEnd->GetAbsOrigin() );
  378. if ( m_flDamage > 0 )
  379. {
  380. trace_t tr;
  381. UTIL_TraceLine( pStart->GetAbsOrigin(), pEnd->GetAbsOrigin(), MASK_SOLID, NULL, COLLISION_GROUP_NONE, &tr );
  382. BeamDamageInstant( &tr, m_flDamage );
  383. }
  384. }
  385. class CTraceFilterPlayersNPCs : public ITraceFilter
  386. {
  387. public:
  388. bool ShouldHitEntity( IHandleEntity *pServerEntity, int contentsMask )
  389. {
  390. CBaseEntity *pEntity = EntityFromEntityHandle( pServerEntity );
  391. if ( pEntity )
  392. {
  393. if ( pEntity->IsPlayer() || pEntity->MyNPCPointer() )
  394. return true;
  395. }
  396. return false;
  397. }
  398. virtual TraceType_t GetTraceType() const
  399. {
  400. return TRACE_ENTITIES_ONLY;
  401. }
  402. };
  403. class CTraceFilterPlayersNPCsPhysicsProps : public ITraceFilter
  404. {
  405. public:
  406. bool ShouldHitEntity( IHandleEntity *pServerEntity, int contentsMask )
  407. {
  408. CBaseEntity *pEntity = EntityFromEntityHandle( pServerEntity );
  409. if ( pEntity )
  410. {
  411. if ( pEntity->IsPlayer() || pEntity->MyNPCPointer() || pEntity->m_iClassname == g_iszPhysicsPropClassname )
  412. return true;
  413. }
  414. return false;
  415. }
  416. virtual TraceType_t GetTraceType() const
  417. {
  418. return TRACE_ENTITIES_ONLY;
  419. }
  420. };
  421. //-----------------------------------------------------------------------------
  422. //-----------------------------------------------------------------------------
  423. bool CEnvBeam::PassesTouchFilters(CBaseEntity *pOther)
  424. {
  425. bool fPassedSoFar = false;
  426. // Touched some player or NPC!
  427. if( m_TouchType != touch_npc_only )
  428. {
  429. if( pOther->IsPlayer() )
  430. {
  431. fPassedSoFar = true;
  432. }
  433. }
  434. if( m_TouchType != touch_player_only )
  435. {
  436. if( pOther->IsNPC() )
  437. {
  438. fPassedSoFar = true;
  439. }
  440. }
  441. if( m_TouchType == touch_player_or_npc_or_physicsprop )
  442. {
  443. if( pOther->m_iClassname == g_iszPhysicsPropClassname )
  444. {
  445. fPassedSoFar = true;
  446. }
  447. }
  448. if( fPassedSoFar )
  449. {
  450. CBaseFilter* pFilter = (CBaseFilter*)(m_hFilter.Get());
  451. return (!pFilter) ? true : pFilter->PassesFilter( this, pOther );
  452. }
  453. return false;
  454. }
  455. //-----------------------------------------------------------------------------
  456. // Purpose:
  457. //-----------------------------------------------------------------------------
  458. void CEnvBeam::UpdateThink( void )
  459. {
  460. // Apply damage every 1/10th of a second.
  461. if ( ( m_flDamage > 0 ) && ( gpGlobals->curtime >= m_flFireTime + 0.1 ) )
  462. {
  463. trace_t tr;
  464. UTIL_TraceLine( GetAbsStartPos(), GetAbsEndPos(), MASK_SOLID, NULL, COLLISION_GROUP_NONE, &tr );
  465. BeamDamage( &tr );
  466. // BeamDamage calls RelinkBeam, so no need to call it again.
  467. }
  468. else
  469. {
  470. RelinkBeam();
  471. }
  472. if( m_TouchType != touch_none )
  473. {
  474. trace_t tr;
  475. Ray_t ray;
  476. ray.Init( GetAbsStartPos(), GetAbsEndPos() );
  477. if( m_TouchType == touch_player_or_npc_or_physicsprop )
  478. {
  479. CTraceFilterPlayersNPCsPhysicsProps traceFilter;
  480. enginetrace->TraceRay( ray, MASK_SHOT, &traceFilter, &tr );
  481. }
  482. else
  483. {
  484. CTraceFilterPlayersNPCs traceFilter;
  485. enginetrace->TraceRay( ray, MASK_SHOT, &traceFilter, &tr );
  486. }
  487. if( tr.fraction != 1.0 && PassesTouchFilters( tr.m_pEnt ) )
  488. {
  489. m_OnTouchedByEntity.FireOutput( tr.m_pEnt, this, 0 );
  490. return;
  491. }
  492. }
  493. SetNextThink( gpGlobals->curtime );
  494. }
  495. //-----------------------------------------------------------------------------
  496. // Purpose:
  497. // Input : &vecSrc -
  498. // &vecDest -
  499. //-----------------------------------------------------------------------------
  500. void CEnvBeam::Zap( const Vector &vecSrc, const Vector &vecDest )
  501. {
  502. CBroadcastRecipientFilter filter;
  503. te->BeamPoints( filter, 0.0,
  504. &vecSrc,
  505. &vecDest,
  506. m_spriteTexture,
  507. 0, // No halo
  508. m_frameStart,
  509. (int)m_flFrameRate,
  510. m_life,
  511. m_boltWidth,
  512. m_boltWidth, // End width
  513. 0, // No fade
  514. m_noiseAmplitude,
  515. m_clrRender->r,
  516. m_clrRender->g,
  517. m_clrRender->b,
  518. m_clrRender->a,
  519. m_speed );
  520. DoSparks( vecSrc, vecDest );
  521. }
  522. //-----------------------------------------------------------------------------
  523. // Purpose:
  524. //-----------------------------------------------------------------------------
  525. void CEnvBeam::RandomArea( void )
  526. {
  527. int iLoops = 0;
  528. for (iLoops = 0; iLoops < 10; iLoops++)
  529. {
  530. Vector vecSrc = GetAbsOrigin();
  531. Vector vecDir1 = Vector( random->RandomFloat( -1.0, 1.0 ), random->RandomFloat( -1.0, 1.0 ),random->RandomFloat( -1.0, 1.0 ) );
  532. VectorNormalize( vecDir1 );
  533. trace_t tr1;
  534. UTIL_TraceLine( vecSrc, vecSrc + vecDir1 * m_radius, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr1 );
  535. if (tr1.fraction == 1.0)
  536. continue;
  537. Vector vecDir2;
  538. do {
  539. vecDir2 = Vector( random->RandomFloat( -1.0, 1.0 ), random->RandomFloat( -1.0, 1.0 ),random->RandomFloat( -1.0, 1.0 ) );
  540. } while (DotProduct(vecDir1, vecDir2 ) > 0);
  541. VectorNormalize( vecDir2 );
  542. trace_t tr2;
  543. UTIL_TraceLine( vecSrc, vecSrc + vecDir2 * m_radius, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr2 );
  544. if (tr2.fraction == 1.0)
  545. continue;
  546. if ((tr1.endpos - tr2.endpos).Length() < m_radius * 0.1)
  547. continue;
  548. UTIL_TraceLine( tr1.endpos, tr2.endpos, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr2 );
  549. if (tr2.fraction != 1.0)
  550. continue;
  551. Zap( tr1.endpos, tr2.endpos );
  552. break;
  553. }
  554. }
  555. //-----------------------------------------------------------------------------
  556. // Purpose:
  557. // Input : vecSrc -
  558. //-----------------------------------------------------------------------------
  559. void CEnvBeam::RandomPoint( const Vector &vecSrc )
  560. {
  561. int iLoops = 0;
  562. for (iLoops = 0; iLoops < 10; iLoops++)
  563. {
  564. Vector vecDir1 = Vector( random->RandomFloat( -1.0, 1.0 ), random->RandomFloat( -1.0, 1.0 ),random->RandomFloat( -1.0, 1.0 ) );
  565. VectorNormalize( vecDir1 );
  566. trace_t tr1;
  567. UTIL_TraceLine( vecSrc, vecSrc + vecDir1 * m_radius, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr1 );
  568. if ((tr1.endpos - vecSrc).Length() < m_radius * 0.1)
  569. continue;
  570. if (tr1.fraction == 1.0)
  571. continue;
  572. Zap( vecSrc, tr1.endpos );
  573. break;
  574. }
  575. }
  576. //-----------------------------------------------------------------------------
  577. // Purpose:
  578. //-----------------------------------------------------------------------------
  579. void CEnvBeam::BeamUpdateVars( void )
  580. {
  581. CBaseEntity *pStart = gEntList.FindEntityByName( NULL, m_iszStartEntity );
  582. CBaseEntity *pEnd = gEntList.FindEntityByName( NULL, m_iszEndEntity );
  583. if (( pStart == NULL ) || ( pEnd == NULL ))
  584. {
  585. return;
  586. }
  587. m_nNumBeamEnts = 2;
  588. m_speed = clamp( (int) m_speed, 0, (int) MAX_BEAM_SCROLLSPEED );
  589. // NOTE: If the end entity is the beam itself (and the start entity
  590. // isn't *also* the beam itself, we've got problems. This is a problem
  591. // because SetAbsStartPos actually sets the entity's origin.
  592. if ( ( pEnd == this ) && ( pStart != this ) )
  593. {
  594. DevMsg("env_beams cannot have the end entity be the beam itself\n"
  595. "unless the start entity is also the beam itself!\n" );
  596. Assert(0);
  597. }
  598. SetModelName( m_iszSpriteName );
  599. SetTexture( m_spriteTexture );
  600. SetType( BEAM_ENTPOINT );
  601. if ( IsStaticPointEntity( pStart ) )
  602. {
  603. SetAbsStartPos( pStart->GetAbsOrigin() );
  604. }
  605. else
  606. {
  607. SetStartEntity( pStart );
  608. }
  609. if ( IsStaticPointEntity( pEnd ) )
  610. {
  611. SetAbsEndPos( pEnd->GetAbsOrigin() );
  612. }
  613. else
  614. {
  615. SetEndEntity( pEnd );
  616. }
  617. RelinkBeam();
  618. SetWidth( MIN(MAX_BEAM_WIDTH, m_boltWidth) );
  619. SetNoise( MIN(MAX_BEAM_NOISEAMPLITUDE, m_noiseAmplitude) );
  620. SetFrame( m_frameStart );
  621. SetScrollRate( m_speed );
  622. if ( m_spawnflags & SF_BEAM_SHADEIN )
  623. {
  624. SetBeamFlags( FBEAM_SHADEIN );
  625. }
  626. else if ( m_spawnflags & SF_BEAM_SHADEOUT )
  627. {
  628. SetBeamFlags( FBEAM_SHADEOUT );
  629. }
  630. }